Compare commits

...
Author SHA1 Message Date
nala c1f4a71963 docs: record installed anchor integration and exact territorial recovery 2026-10-23 19:26:07 +02:00
nala cf2ddf21a1 fix: verify territorial recovery manifest with canonical Git line endings
tests / ci (push) Canceled after 0s
2026-10-23 19:20:25 +02:00
nala 3fd5128c62 fix: connect documentary anchors to investment preparation and recover accepted territories
tests / ci (push) Canceled after 0s
2026-10-23 19:17:15 +02:00
nala cdf8fcfba4 docs: record installed treatment scope and verified native PDF 2026-10-23 18:50:13 +02:00
nala 8036b9b071 docs: verify saved native PDF and release delivery condition
tests / ci (push) Canceled after 0s
2026-10-23 18:44:09 +02:00
nala 6985f82e9c docs: delimit anchor findings and pending native PDF delivery gate 2026-10-23 18:36:08 +02:00
nala 2856240cf4 fix: print selected requirements and translated opportunity stage states 2026-10-23 18:34:39 +02:00
nala 32144aa2f3 docs: record treatment scope QA and anchor search analysis boundary 2026-10-23 18:26:00 +02:00
nala 1afdfee8c0 fix: share selected opportunity across simulator stages and land shortcuts 2026-10-23 18:25:09 +02:00
nala bc84fbdb3e docs: delimit remaining transversal investment scope 2026-10-23 18:06:42 +02:00
nala b6a626c92d Record installed opportunity-consumer delta in homologation 2026-10-23 17:58:15 +02:00
nala 55136b446d Record directed QA proof for all four opportunity consumers
tests / ci (push) Canceled after 0s
2026-10-23 17:51:45 +02:00
nala 4a01e9e2b7 Scope parks and downstream investment requests to the treated opportunity 2026-10-23 17:49:22 +02:00
nala ad4160f040 Record second-opportunity context assessment and bounded consumer gaps 2026-10-23 17:18:55 +02:00
nala af9261809c Record installed complements delta and preserved acceptance 2026-10-23 17:14:04 +02:00
nala d8224ba84b Align public simulator entry check with integrated home layout
tests / ci (push) Canceled after 0s
2026-10-23 17:11:15 +02:00
nala 088f2c2cf4 Merge remote-tracking branch 'origin/main' into codex/investment-complements-20261023
tests / ci (push) Canceled after 0s
2026-10-23 16:35:03 +02:00
nala 603a88e1d3 Link documented investment complements to existing fiches and dossier context 2026-10-23 16:34:57 +02:00
paulomassigue 43cd0be7cb correcao de conflitos
tests / ci (push) Canceled after 0s
2026-10-23 16:03:34 +02:00
paulomassigue 89a9c3d994 mudanca o botao Tempo no hear fica Mais 2026-10-23 15:58:59 +02:00
nala 4098192a05 Record installed continuity corrections and destination delta checks 2026-10-23 15:48:52 +02:00
paulomassigue d242482042 home ajustado 2026-10-23 15:46:53 +02:00
nala 6b544548d3 Fix compressed draft saves provenance and Beira documentary continuity
tests / ci (push) Canceled after 0s
2026-10-23 15:41:37 +02:00
nala c3f7c3ad10 Record homologation product journey findings and publication status 2026-10-23 15:26:38 +02:00
nala 445aa7e69d Record consolidated adaptive portfolio installation receipt 2026-10-23 15:17:59 +02:00
nala 7c1f532134 Preserve inactive document history in adaptive reports
tests / ci (push) Canceled after 0s
2026-10-23 15:14:44 +02:00
nala 115c773162 Record QA continuity recovery after return reload and login
tests / ci (push) Canceled after 0s
2026-10-23 15:03:22 +02:00
paulomassigue ec4b2f2af2 organizacao da tela inicial 2026-10-23 14:51:34 +02:00
nala 3d877ec745 Preserve selected portfolio context through investment preparation 2026-10-23 14:35:50 +02:00
nala 6948ed62fc Consolidate adaptive preparation QA delivery and accepted evidence 2026-10-23 14:19:05 +02:00
nala 7d798579a6 Reconcile portfolio recovery evidence and integrated journey gap 2026-10-23 14:17:42 +02:00
nala 5bda74aedc Adapt land status to saved investment land need 2026-10-23 14:13:58 +02:00
nala f2150b1912 Reuse investment context as editable simulator summaries 2026-10-23 14:03:37 +02:00
nala 7ca94967f3 Adapt preparation presentation to declared applicability 2026-10-23 13:53:02 +02:00
nala e1f010f941 fix: preserve corridor filters and keep investment previews readable
tests / ci (push) Canceled after 0s
2026-10-23 13:30:10 +02:00
nala 0025309b98 feat: adapt investment territories to Tempo corridor configuration
tests / ci (push) Canceled after 0s
2026-10-23 13:24:58 +02:00
nala a0e80db4ec docs: confirm opportunity preparation continuity in homologation
tests / ci (push) Canceled after 0s
2026-10-23 12:31:36 +02:00
nala 7226287526 fix: keep opportunity preparation focused on the pending selection
tests / ci (push) Canceled after 0s
2026-10-23 12:29:26 +02:00
nala f1ac82ae4d docs: record clean investor entry verified in homologation
tests / ci (push) Canceled after 0s
2026-10-23 12:20:28 +02:00
nala 98474777f6 fix: remove internal metadata from investor dossier presentation
tests / ci (push) Canceled after 0s
2026-10-23 12:18:25 +02:00
nala 3848f29bea docs: record corridor catalogue verification in homologation
tests / ci (push) Canceled after 0s
2026-10-23 12:12:05 +02:00
nala 203492997e feat: explore the public opportunity catalogue by corridor
tests / ci (push) Canceled after 0s
2026-10-23 12:08:33 +02:00
nala 165c2f6171 fix: keep internal agribusiness delivery notes out of portal presentation
tests / ci (push) Canceled after 0s
2026-10-23 11:22:39 +02:00
nala 4ad1bf402b docs: record authenticated publication of 34 agribusiness opportunities
tests / ci (push) Canceled after 0s
2026-10-23 11:12:45 +02:00
nala e12ef6ad42 Record installed agribusiness workflow, preserved destination and pending real-account publication
tests / ci (push) Canceled after 0s
2026-10-23 11:00:30 +02:00
nala c641b267ac Publish agribusiness lot through scoped management and extend existing dossier for multiple opportunities
tests / ci (push) Canceled after 0s
2026-10-23 10:49:50 +02:00
paulomassigue e2b0fb4b2d melhorias de home 1 2026-10-23 10:36:34 +02:00
nala 7d08c2c664 Register 34 private agribusiness opportunities with promotional management and investor paths in QA 2026-10-23 10:15:41 +02:00
nala 761035aefb Record homologation agribusiness installation and preserved QA evidence
tests / ci (push) Canceled after 0s
2026-10-23 09:58:13 +02:00
nala 20a4099d10 Present 34 agribusiness opportunities in existing corridor sections
tests / ci (push) Canceled after 0s
2026-10-23 09:56:22 +02:00
nala e03de682ca Preserve completed features and restore approved portfolio visibility
tests / ci (push) Canceled after 0s
2026-10-23 09:19:25 +02:00
nala 37a8eb015a docs: record private legacy registration and authenticated editorial access
tests / ci (push) Canceled after 0s
2026-10-22 23:55:57 +02:00
nala 65eddf6bc7 merge: install reviewed private legacy editorial workflow
tests / ci (push) Canceled after 0s
2026-10-22 23:40:27 +02:00
nala fb4e7d8970 Complete private legacy article editing and previews in QA 2026-10-22 23:36:07 +02:00
nala bc93ccdb9e docs: reconcile all legacy articles and verify private QA batches 2026-10-22 23:10:21 +02:00
nala 7ccf62adec docs: record authenticated tender installation and final links
tests / ci (push) Canceled after 0s
2026-10-22 22:54:59 +02:00
nala f4d0970917 merge: install reviewed tender publishing in homologation
tests / ci (push) Canceled after 0s
2026-10-22 22:49:18 +02:00
nala e46f973159 docs: plan staged tender schema installation and activation 2026-10-22 22:44:42 +02:00
nala 1bcf34c0c0 feat: add QA tender and announcement publishing workflow 2026-10-22 22:41:28 +02:00
nala ef0c2acd36 docs: record authenticated homologation geometry restoration and access proof
tests / ci (push) Canceled after 0s
2026-10-22 22:04:32 +02:00
nala 30aed7cd12 Merge approved administrative access for geometry restoration
tests / ci (push) Canceled after 0s
2026-10-22 21:55:48 +02:00
nala 95d4cf44d7 fix: reuse approved administration for authenticated geometry import 2026-10-22 21:55:36 +02:00
nala 73c046e104 Merge territorial demarcation recovery reviewed for homologation
tests / ci (push) Canceled after 0s
2026-10-22 21:45:58 +02:00
nala 15d82611b6 fix: recover territorial demarcation QA and prepare retained geometry restoration 2026-10-22 21:43:54 +02:00
nala 8286d7504d fix: preserve approved editorial access independently of technical reservation
tests / ci (push) Canceled after 0s
2026-10-22 21:03:11 +02:00
nala aa2b3d05b0 fix: route authorised content staff through management access entry
tests / ci (push) Waiting to run
2026-10-22 21:00:05 +02:00
nala 97d51db60a feat: add unified team content management entry in QA
tests / ci (push) Canceled after 0s
2026-10-22 20:55:08 +02:00
Nelson_Matsinhe a1b553372e ajuste do tamanho da navbar
tests / ci (push) Canceled after 0s
2026-10-22 15:26:56 +02:00
nala f80d30f478 docs: record Tempo approval for catalogue selector integration
tests / ci (push) Canceled after 0s
2026-10-22 14:36:36 +02:00
nala 916819eb35 docs: record keyboard selector proof and mouse observation limit 2026-10-22 14:33:59 +02:00
nala 1d921c9d03 fix: keep public catalogue selectors independent of opportunity results 2026-10-22 14:28:40 +02:00
nala 7b1c35793c Fix Node navigation import and use Tempo commercial footer contact
tests / ci (push) Canceled after 0s
2026-10-22 13:54:20 +02:00
nala 1ec318561c docs: record authorised Inhambane preparation publication
tests / ci (push) Canceled after 0s
2026-10-22 13:47:50 +02:00
nala 9ac623a433 fix: preserve territorial search across investment catalogues 2026-10-22 12:52:52 +02:00
273 changed files with 22290 additions and 984 deletions
+16
View File
@@ -0,0 +1,16 @@
.git
.idea
.agents
.codex
node_modules
vendor
public/build
storage/logs
storage/framework/cache
storage/framework/sessions
storage/framework/views
tests
Doc
docs
*.zip
.phpunit.result.cache
+18
View File
@@ -1,5 +1,13 @@
# Instruções permanentes do projeto InvesteMoz
## Percurso adaptativo — decisão expressa da Tempo, 23/10/2026
Carteira/projectos âncora e percurso adaptativo têm diagnósticos distintos, mas fazem parte da mesma experiência e responsabilidade de correcção. Separar o diagnóstico não autoriza adiamento, desligamento do percurso ou perda de contexto entre corredor/localização, oportunidades, associações documentadas a âncoras/clusters, dossier e simulador. A reposição prioritária só se considera demonstrada com causa delimitada e disponibilidade recuperada, reutilizando o inventário e os recibos existentes; melhorias noutro ponto não a comprovam. Consultar os registos de reposição antes de voltar a declarar causa desconhecida ou registos desaparecidos.
Apresentar apenas o necessário ao investimento em preparação, com entrada por ideia, oportunidade, sector ou localização, reutilizando informação válida e autorizada da oportunidade, perfil, organização, dossier e fontes anteriores. As etapas são adaptáveis, não seis formulários obrigatórios. Não sobrescrever edições deliberadas; explicar divergências e impactos antes da confirmação. Conservar escolhas ao avançar, regressar, actualizar e autenticar-se. Seleccionar opções únicas e pedir apenas dados em falta ou desactualizados.
Ocultar campos e acções sem aplicação, conservando explicação breve e histórico; desconhecido não significa não aplicável. Na selecção múltipla, partilhar dados comuns sem perder os requisitos específicos de cada oportunidade. Consulta pública livre; autenticação no momento de manifestar interesse ou iniciar operação privada. Relatórios e exportações devem reflectir o contexto e as etapas efectivamente aplicáveis. O utilizador deve perceber onde está, o que falta e a próxima acção. Aperfeiçoar os componentes existentes por incrementos e registar cobertura na matriz existente; esta instrução não declara concluída a sua implementação transversal.
## Regras permanentes do InvesteMoz — decisão da Tempo, 12/10/2026
1. O portal InvesteMoz é propriedade da Tempo, Lda.
@@ -46,3 +54,13 @@ Referenciar estas orientações nos requisitos correspondentes da matriz existen
A legislação, os formulários e os procedimentos oficiais disponibilizados são fontes válidas para implementar os requisitos. As decisões e procedimentos da Tempo orientam a sua utilização no portal. Traduzir essas fontes em campos, regras, documentos, cálculos e percursos executáveis com rastreabilidade, sem exigir nova validação jurídica genérica ou criar aprovadores, restrições ou condições por interpretação do Codex.
Uma divergência concreta deve identificar os trechos, períodos e comportamento afectados; prosseguir com os restantes requisitos. Uma decisão de implementação em falta deve ser apresentada especificamente à Tempo, sem a substituir por regra inventada. Rever e remover bloqueios anteriores sem fundamento numa fonte aplicável ou decisão expressa da Tempo, preservando dados e histórico. A rastreabilidade explica a execução e não constitui uma autoridade adicional. Mantêm-se os limites autorizados de QA e das alterações no activo.
## Preservação do concluído — pedido expresso da Tempo, 23/10/2026
Antes de avançar com novas rondas de testes/validações, inventariar na matriz e no manual existentes as funcionalidades concluídas e as funcionalidades desaparecidas, e repor as perdas confirmadas. Preservar as aprovações, dados, originais, relações, versões, histórico e acessos já concluídos. Não transformar uma entrega anterior em funcionalidade nova nem exigir novamente a decisão organizacional já registada.
Comparar código, schema, dados, configuração e permissões entre o último estado demonstrado e o destino. Distinguir eliminação, perda de visibilidade, diferença de ambiente, pendência real e estado documental desactualizado; uma página vazia ou a falta de nova prova não apagam a aceitação anterior. Reutilizar as provas aceites e conferir apenas o delta da reposição. Cada entrega seguinte deve explicar qualquer redução do âmbito previamente disponível, em vez de a aceitar como consequência normal da implementação.
Transporte privado não equivale à revogação de uma divulgação anterior aprovada. Reconciliar a decisão anterior de forma delimitada e auditada, sem publicar automaticamente conteúdos novos, reviver exemplares arquivados ou sobrescrever edições posteriores. Mantêm-se backup/restauro, autenticação e os limites concretos de cada tarefa.
Escolha técnica de verificação: comparar dados protegidos por segurança por linha (RLS) com identidades e contexto de acesso equivalentes. Uma leitura da aplicação sem actor pode ocultar registos que o utilizador do backup consegue ler; confirmar essa diferença antes de declarar perda ou propor reposição. Esta orientação não altera permissões do portal nem autoriza contornar RLS numa operação da equipa.
File diff suppressed because one or more lines are too long
Binary file not shown.
+342
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1,73 @@
<?php
namespace App\Http\Controllers;
use App\Models\Opportunity;
use App\Services\CorridorAgribusinessOpportunities;
use App\Services\OpportunityManagement;
use App\Services\OpportunityPromotion;
use App\Services\PortalProfileAccess;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
final class CorridorAgribusinessController extends Controller
{
private function manifest(CorridorAgribusinessOpportunities $service): string
{
return hash('sha256', json_encode($service->entries(), JSON_THROW_ON_ERROR));
}
public function show(Request $request, CorridorAgribusinessOpportunities $service)
{
$access = app(PortalProfileAccess::class);
abort_unless($access->internalAssignment($request->user(), 'investment_project') || $access->has($request->user(), 'publisher'), 403);
$records = Opportunity::withTrashed()->whereIn('management_creation_key', array_column($service->entries(), 'key'))->get()->keyBy('management_creation_key');
foreach ($records as $record) abort_unless($access->canView($request->user(), $record), 403);
return Inertia::render('management/corridor-agribusiness', ['manifestHash' => $this->manifest($service),
'canRegister' => (bool) $access->internalAssignment($request->user(), 'investment_project'),
'entries' => array_map(function ($entry) use ($records) {
$r = $records->get($entry['key']);
return [...$entry, 'id' => $r?->id, 'status' => $r?->status, 'href' => $r ? '/oportunidades/'.$r->slug : null];
}, $service->entries())]);
}
public function register(Request $request, CorridorAgribusinessOpportunities $service)
{
$data = $request->validate(['manifest_hash' => 'required|string|size:64', 'confirm' => 'required|accepted']);
abort_unless(hash_equals($this->manifest($service), $data['manifest_hash']), 409, 'A fonte mudou; recupere a prévia.');
$access = app(PortalProfileAccess::class);
$grant = $access->internalAssignment($request->user(), 'investment_project');
abort_unless($grant && $access->mfa($request->user()), 403, 'Use a designação existente e MFA recente.');
return response()->json(['records' => $service->register($request->user(), $grant->organization_id)]);
}
public function preview(Request $request, CorridorAgribusinessOpportunities $service)
{
$previews = [];
foreach ($service->entries() as $entry) {
$record = Opportunity::where('management_creation_key', $entry['key'])->firstOrFail();
$input = data_get($record->workflow_context, 'promotional_summary.input');
abort_unless(is_array($input), 409, 'Complete a selecção pública na divulgação individual.');
$previews[] = ['id' => $record->id, ...app(OpportunityPromotion::class)->preview($request->user(), $record,
[...$input, 'source_date' => $input['source_date'] ?: null, 'action' => 'disclose', 'etag' => app(OpportunityManagement::class)->etag($record)])];
}
return response()->json(['previews' => $previews]);
}
public function disclose(Request $request, CorridorAgribusinessOpportunities $service)
{
$data = $request->validate(['confirm' => 'required|accepted', 'reason' => 'required|string|min:20|max:1000',
'previews' => 'required|array|size:34', 'previews.*.id' => 'required|integer|distinct',
'previews.*.token' => 'required|string|max:300000', 'previews.*.operation_key' => 'required|uuid|distinct']);
$expected = Opportunity::whereIn('management_creation_key', array_column($service->entries(), 'key'))->pluck('id')->sort()->values()->all();
$supplied = array_column($data['previews'], 'id'); sort($supplied);
abort_unless($expected === $supplied && count($expected) === 34, 409, 'A prévia deve identificar exactamente as 34 fichas deste lote.');
return response()->json(DB::transaction(function () use ($request, $data) {
$result = [];
foreach ($data['previews'] as $preview) $result[] = app(OpportunityPromotion::class)->confirm($request->user(), $preview['id'],
[...$preview, 'confirm' => true, 'reason' => $data['reason']]);
return ['records' => $result];
}));
}
}
@@ -81,31 +81,18 @@ class EditorialArticleController extends Controller
app(\App\Services\DocumentManagementAccess::class)->assertEditableResource(request()->user(), $id);
$r = DB::table('portal_resources')->find($id);
abort_unless($r, 404);
$content = $r->editorial_content ? json_decode($r->editorial_content, true) : null;
if ($content === null) {
$document = DB::table('documents')->find($r->document_id);
if ($document && $document->status === 'active' && $document->malware_scan_status === 'clean'
&& in_array($document->media_type, ['text/plain', 'application/json'], true)) {
$binary = app(VerifiedDocumentBinary::class)->respond($document);
$bytes = $binary instanceof \Symfony\Component\HttpFoundation\BinaryFileResponse
? file_get_contents($binary->getFile()->getPathname()) : $binary->getContent();
$original = json_decode($bytes, true);
if (is_array($original) && ($original['format'] ?? '') === 'investmoz-legacy-article-v1') {
$html = preg_replace('~</(?:p|div|li|h[1-6])>|<br\s*/?>~i', "\n\n", $original['content_html'] ?? '');
$content = ['body' => trim(html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8')), 'image_resource_id' => null];
}
}
}
return Inertia::render('portal/article-editor', ['resource' => ['id' => $r->id, 'title' => $r->title, 'metadata_version' => $r->metadata_version],
'content' => $content, 'images' => $this->publicResources()->whereIn('d.media_type', ['image/png', 'image/jpeg', 'image/webp'])->get(['r.id', 'r.title']),
'revisions' => DB::table('portal_resource_revisions')->where('resource_id', $id)->orderByDesc('revision_no')->get(['revision_no', 'change_type', 'created_at'])]);
$reading = app(\App\Services\EditorialReading::class);
$original = $reading->original($r);
$content = $reading->content($r, $original);
return Inertia::render('portal/article-editor', ['resource' => ['id' => $r->id, 'title' => $r->title, 'metadata_version' => $r->metadata_version, 'published' => (bool) $r->published],
'content' => $content, 'source' => $original ? array_intersect_key($original, array_flip(['source_url', 'published_at', 'modified_at', 'legacy_id', 'image_source_url'])) : null, 'images' => $this->publicResources()->whereIn('d.media_type', ['image/png', 'image/jpeg', 'image/webp'])->get(['r.id', 'r.title']),
'revisions' => DB::table('portal_resource_revisions')->where('resource_id', $id)->orderByDesc('revision_no')->get(['revision_no', 'change_type', 'created_at'])])->toResponse(request())->header('Cache-Control', 'private, no-store');
}
public function save(Request $request, int $id)
{
$this->installed();
$data = $request->validate(['body' => ['required', 'string', 'max:100000'], 'image_resource_id' => ['nullable', 'integer'], 'metadata_version' => ['required', 'integer', 'min:1']]);
$data = $this->validateContent($request);
DB::transaction(function () use ($request, $id, $data) {
$r = DB::table('portal_resources')->where('id', $id)->lockForUpdate()->first();
abort_unless($r, 404);
@@ -118,7 +105,7 @@ class EditorialArticleController extends Controller
if ($body === '') {
throw ValidationException::withMessages(['body' => 'Escreva o corpo do artigo.']);
}
$content = ['format' => 1, 'body' => $body, 'image_resource_id' => $data['image_resource_id'] ?? null];
$content = $this->edition($r, $data);
$revision = (int) $r->metadata_version + 1;
DB::table('portal_resources')->where('id', $id)->update(['editorial_content' => json_encode($content, JSON_THROW_ON_ERROR), 'published' => false, 'approved_by' => null, 'metadata_version' => $revision, 'last_reviewed_by' => $request->user()->id, 'last_reviewed_at' => now(), 'updated_at' => now()]);
$resource = DB::table('portal_resources')->find($id);
@@ -130,16 +117,46 @@ class EditorialArticleController extends Controller
return redirect('/management/portal-services/resources/'.$id.'/article')->with('success', 'Artigo guardado como rascunho. Publique expressamente no acervo quando estiver pronto.');
}
public function show(int $id)
private function validateContent(Request $request): array
{
return $request->validate(['body' => ['required', 'string', 'max:100000'], 'image_resource_id' => ['nullable', 'integer'], 'image_mode' => ['nullable', 'in:original,selected,none'], 'metadata_version' => ['required', 'integer', 'min:1']]);
}
private function edition(object $resource, array $data): array
{
$reading = app(\App\Services\EditorialReading::class);
$original = $reading->original($resource);
$mode = $data['image_mode'] ?? (!empty($data['image_resource_id']) ? 'selected' : 'none');
if ($mode === 'selected') abort_unless(!empty($data['image_resource_id']) && $reading->publicResources()->where('r.id', $data['image_resource_id'])->whereIn('d.media_type', ['image/png', 'image/jpeg', 'image/webp'])->exists(), 422, 'Escolha uma imagem pública disponível na biblioteca.');
return ['format' => 1, 'body' => trim($data['body']), 'image_mode' => $mode, 'image_resource_id' => $mode === 'selected' ? (int)$data['image_resource_id'] : null,
'preserve_original_html' => $original && trim($data['body']) === $reading->plain($original['content_html'])];
}
public function preview(Request $request, int $id)
{
$this->installed();
$r = $this->publicResources()->where('r.id', $id)->first(['r.id', 'r.title', 'r.description', 'r.editorial_content', 'r.document_id', 'd.issuer']);
abort_unless($r && $r->editorial_content, 404);
$content = json_decode($r->editorial_content, true);
// Recheck the original bytes using the same binary gate as the library.
$binary = app(VerifiedDocumentBinary::class)->respond($this->publicResources()->where('r.id', $id)->first(['r.*', 'd.*']));
$image = ! empty($content['image_resource_id']) ? $this->publicResources()->where('r.id', $content['image_resource_id'])->whereIn('d.media_type', ['image/png', 'image/jpeg', 'image/webp'])->value('r.id') : null;
app(\App\Services\DocumentManagementAccess::class)->assertEditableResource($request->user(), $id);
$resource = DB::table('portal_resources')->find($id); abort_unless($resource, 404);
return Inertia::render('portal/article', ['article' => app(\App\Services\EditorialReading::class)->reading($resource, null, true)])
->toResponse($request)->header('Cache-Control', 'private, no-store')->header('X-Robots-Tag', 'noindex, nofollow');
}
return Inertia::render('portal/article', ['article' => ['id' => $id, 'title' => $r->title, 'description' => $r->description, 'author' => $r->issuer, 'body' => $content['body'], 'image_url' => $image ? '/documentos/'.$image.'/view' : null, 'original_url' => '/documentos/'.$id]]);
public function previewContent(Request $request, int $id)
{
$this->installed();
app(\App\Services\DocumentManagementAccess::class)->assertEditableResource($request->user(), $id);
$resource = DB::table('portal_resources')->find($id); abort_unless($resource, 404);
$data = $this->validateContent($request);
abort_unless((int)$resource->metadata_version === (int)$data['metadata_version'], 409, 'A revisão mudou; recupere antes de rever a prévia.');
return response()->json(['article' => app(\App\Services\EditorialReading::class)->reading($resource, $this->edition($resource, $data), true)])
->header('Cache-Control', 'private, no-store')->header('X-Robots-Tag', 'noindex, nofollow');
}
public function show(Request $request, int $id)
{
$this->installed();
$resource = app(\App\Services\EditorialReading::class)->publicResources()->where('r.id', $id)->first(['r.*']);
abort_unless($resource, 404);
return Inertia::render('portal/article', ['article' => app(\App\Services\EditorialReading::class)->reading($resource)])->toResponse($request)->header('Cache-Control', 'private, no-store');
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers;
use App\Services\InvestmentComplements;
use Illuminate\Http\Request;
final class InvestmentComplementController extends Controller
{
public function index(Request $request, InvestmentComplements $service)
{
$data = $request->validate(['origin' => [\Illuminate\Validation\Rule::requiredIf(!$request->has('contexts')), 'nullable', 'string', 'max:100'], 'territory_id' => 'nullable|uuid',
'contexts' => 'sometimes|array|max:100', 'contexts.*' => 'array:origin,territory_id', 'contexts.*.origin' => 'required|string|max:100', 'contexts.*.territory_id' => 'nullable|uuid']);
$items = [];
$issues = [];
foreach ($data['contexts'] ?? [['origin' => $data['origin'], 'territory_id' => $data['territory_id'] ?? null]] as $context) {
try {
$items = [...$items, ...$service->options($context['origin'], $context['territory_id'] ?? null)];
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException|\Symfony\Component\HttpKernel\Exception\HttpException $e) {
$status = $e instanceof \Symfony\Component\HttpKernel\Exception\HttpException ? $e->getStatusCode() : 404;
if (!isset($data['contexts']) || $status !== 404) throw $e;
$issues[] = ['origin' => $context['origin'], 'message' => 'Uma referência escolhida deixou de estar disponível para consulta pública. A relação guardada conserva-se no dossier; os outros complementos continuam disponíveis.'];
}
}
return response()->json(isset($data['contexts']) ? ['items' => $items, 'issues' => $issues] : $items)->header('Cache-Control', 'private, no-store');
}
}
@@ -26,6 +26,7 @@ class InvestmentDiscoveryController extends Controller
}
return Inertia::render('investment/catalogue', ['filters' => $filters, 'catalogueUrl' => $request->routeIs('investment.education') ? '/educacao' : '/oportunidades', 'references' => $catalogue->references(false, $filters),
'corridorGroups' => ($filters['view'] ?? '') === 'corridors' ? app(\App\Services\OpportunityCorridorExplorer::class)->groups($filters) : [],
'territorySelection' => app(InvestmentGeography::class)->selection($filters),
'guides' => app(\App\Services\SectorInvestment::class)->guides(\App\Models\Sector::where('slug', $filters['sector'] ?? '')->where('is_active', true)->whereNotNull('published_at')->first()),
'results' => $catalogue->query($filters)->paginate(12)->withQueryString(),
@@ -88,7 +89,7 @@ class InvestmentDiscoveryController extends Controller
$data = $request->validate(['zone_id' => ['nullable', 'uuid'], 'from_dossier' => ['nullable', 'uuid']]);
$dossierOrigin = isset($data['from_dossier']) ? InvestmentPortfolioController::visible()->whereKey($data['from_dossier'])->firstOrFail() : null;
if ($dossierOrigin) {
$linked = false;
$linked = in_array($record->id, app(\App\Services\CorridorAgribusinessOpportunities::class)->portfolioOpportunityIds($dossierOrigin), true);
foreach (['projects' => 'project_id', 'clusters' => 'cluster_id', 'corridors' => 'corridor_id'] as $relation => $foreign) {
if ($dossierOrigin->$foreign && DB::table('opportunity_'.$relation)->where('opportunity_id', $record->id)->where($foreign, $dossierOrigin->$foreign)->exists()) {
$linked = true;
@@ -106,7 +107,7 @@ class InvestmentDiscoveryController extends Controller
$returnFilters = $request->query();
unset($returnFilters['zone_id'], $returnFilters['from_dossier']);
return Inertia::render('investment/detail', ['record' => $record, 'context' => $catalogue->context($record->id), 'zone_origin' => $origin,
return Inertia::render('investment/detail', ['complements' => app(\App\Services\InvestmentComplements::class)->options('opportunity:'.$record->id), 'record' => $record, 'context' => $catalogue->context($record->id), 'zone_origin' => $origin,
'guides' => app(\App\Services\SectorInvestment::class)->guides($record->sector),
'dossierOrigin' => $dossierOrigin ? ['id' => $dossierOrigin->id, 'title' => $dossierOrigin->title] : null,
'returnUrl' => $returnFilters === [] ? '/oportunidades' : '/oportunidades?'.http_build_query($returnFilters)]);
@@ -61,8 +61,15 @@ class InvestmentPortfolioController extends Controller
{
$management = $request->routeIs('management.portfolio*');
$filters = $request->validate(['q' => ['nullable', 'string', 'max:120'], 'kind' => ['nullable', 'in:corridor,cluster,project'],
'sector' => ['nullable', 'string', 'max:500'],
'province_id' => ['nullable', 'uuid'], 'district_id' => ['nullable', 'uuid'], 'admin_post_id' => ['nullable', 'uuid'], 'locality_id' => ['nullable', 'uuid']]);
$query = $management ? InvestmentDossier::query() : self::visible();
$sectorOptions = ($management ? InvestmentDossier::query() : self::visible())
->whereNotNull('content->sector')->get()->pluck('content.sector')
->filter(fn ($sector) => is_string($sector) && trim($sector) !== '')->unique()->sort()->values();
if (! empty($filters['sector'])) {
$query->where('content->sector', $filters['sector']);
}
if (! empty($filters['kind'])) {
$query->whereNotNull($filters['kind'].'_id');
}
@@ -76,7 +83,7 @@ class InvestmentPortfolioController extends Controller
}
return Inertia::render('portfolio/index', ['records' => $query->orderBy('title')->paginate(30)->withQueryString(),
'management' => $management, 'filters' => $filters, 'territorySelection' => $territorySelection]);
'management' => $management, 'filters' => $filters, 'sectors' => $sectorOptions, 'territorySelection' => $territorySelection]);
}
public function show(Request $request, string $id): Response
@@ -105,6 +112,7 @@ class InvestmentPortfolioController extends Controller
$opportunities = Opportunity::published()->where(function ($query) use ($record) {
$query->whereRaw('false');
$query->orWhereIn('id', app(\App\Services\CorridorAgribusinessOpportunities::class)->portfolioOpportunityIds($record));
foreach (['clusters' => 'cluster_id', 'corridors' => 'corridor_id', 'projects' => 'project_id'] as $relation => $foreign) {
if ($record->$foreign) {
$query->orWhereIn('id', DB::table('opportunity_'.$relation)->where($foreign, $record->$foreign)->select('opportunity_id'));
@@ -130,12 +138,19 @@ class InvestmentPortfolioController extends Controller
->orderByDesc('updated_at')->limit(10)->get(['id', 'version', 'updated_at'])
: collect();
return Inertia::render('portfolio/show', ['record' => $record, 'related' => $related, 'management' => $management, 'zones' => $zones,
return Inertia::render('portfolio/show', ['complements' => $management ? [] : app(\App\Services\InvestmentComplements::class)->options('portfolio:'.$record->id), 'record' => $record, 'related' => $related, 'management' => $management, 'zones' => $zones,
'anchorProject' => $anchorProject, 'existingSubmissions' => $existingSubmissions,
'geography' => app(InvestmentRelations::class)->dossier($record, $management),
'origin' => $origin ? ['id' => $origin->id, 'title' => $origin->title] : null, 'opportunities' => $opportunities, 'chains' => $chains]);
}
public function resumeInterest(Request $request, string $id): RedirectResponse
{
self::visible()->findOrFail($id);
$data = $request->validate(['catalogue_query' => ['nullable', 'string', 'max:4000']]);
return redirect('/carteira/'.$id.'?'.http_build_query([...$data, 'prepare_interest' => '1']));
}
public function save(Request $request, InvestmentDossier $dossier): RedirectResponse
{
$data = $request->validate(['version_no' => ['required', 'integer', 'min:1'], 'title' => ['required', 'string', 'max:255'], 'source' => ['required', 'string', 'max:2000'],
@@ -192,11 +192,13 @@ class InvestmentSubmissionController extends Controller
{
$required = $submitting ? 'required' : 'nullable';
$data = $request->validate(['version' => ['required', 'integer', 'min:1'],
'payload' => ['required', 'array:context_reuse,full_name,email,phone,country,residence_country_code,incorporation_country_code,organization_country_snapshot,declared_origin_country_code,organisation,representative_role,experience,investment_capacity,profile_id,sector,objective,participation_id,partnership_id,estimated_amount,currency,horizon,location,cluster_ids,corridor_ids,project_ids,contact_consent,other_details'],
'payload' => ['required', 'array:context_opportunity,context_opportunity_snapshot,context_reuse,full_name,email,phone,country,residence_country_code,incorporation_country_code,organization_country_snapshot,declared_origin_country_code,organisation,representative_role,experience,investment_capacity,profile_id,sector,objective,participation_id,partnership_id,estimated_amount,currency,horizon,location,cluster_ids,corridor_ids,project_ids,contact_consent,other_details'],
'payload.organization_country_snapshot' => ['sometimes', 'array', 'max:10'],
'payload.residence_country_code' => ['nullable', 'string', app(CountryCatalogue::class)->rule()],
'payload.incorporation_country_code' => ['nullable', 'string', app(CountryCatalogue::class)->rule()],
'payload.declared_origin_country_code' => ['nullable', 'string', app(CountryCatalogue::class)->rule()],
'payload.context_opportunity' => ['nullable', 'string', 'max:100'],
'payload.context_opportunity_snapshot' => ['nullable', 'string', 'max:200000'],
'payload.context_reuse' => ['nullable', 'string', 'max:10000'],
'payload.other_details' => ['nullable', 'string', 'max:5000'],
'payload.full_name' => [$required, 'string', ...($submitting ? ['min:3'] : []), 'max:150'], 'payload.email' => [$required, $submitting ? 'email:rfc' : 'string', 'max:150'],
@@ -247,6 +249,9 @@ class InvestmentSubmissionController extends Controller
}
}
$bound = DB::table('desk_cases')->where('submission_id', $request->route('id'))->where('user_id', $request->user()->id)->whereNotNull('journey_id')->first();
if ($bound) $data['payload'] = app(\App\Services\InvestmentConsumerScope::class)->bind($request->user(), $bound->id, $data['payload'], $submitting);
else unset($data['payload']['context_opportunity'], $data['payload']['context_opportunity_snapshot']);
return $data;
}
@@ -266,7 +271,7 @@ class InvestmentSubmissionController extends Controller
$version = DB::transaction(function () use ($request, $id, $data) {
$row = $this->owned($request, $id, true, true);
abort_unless($row->status === 'draft' && $row->version === (int) $data['version'], 409, 'Rascunho alterado. Recarregue antes de guardar.');
$row->update(['payload' => $this->preserveCountrySource($row, $data['payload']), 'version' => $row->version + 1]);
$row->update([...(isset($data['payload']['context_opportunity']) ? ['opportunity_id'=>(int)substr($data['payload']['context_opportunity'],12)] : []), 'payload' => $this->preserveCountrySource($row, $data['payload']), 'version' => $row->version + 1]);
InvestmentSpecificNeed::where('submission_id', $id)->delete();
foreach ($data['needs'] ?? [] as $need) {
InvestmentSpecificNeed::create([...$need, 'submission_id' => $id]);
@@ -310,7 +315,8 @@ class InvestmentSubmissionController extends Controller
return InterestRequest::findOrFail($row->interest_request_id)->reference;
}
abort_unless($row->version === (int) $data['version'], 409, 'Rascunho alterado. Recarregue.');
$opportunity = $row->opportunity_id ? Opportunity::published()->findOrFail($row->opportunity_id) : null;
$scopeKey = $data['payload']['context_opportunity'] ?? null;
$opportunity = $scopeKey ? Opportunity::published()->findOrFail(substr($scopeKey,12)) : ($row->opportunity_id ? Opportunity::published()->findOrFail($row->opportunity_id) : null);
if ($row->dossier_id) {
InvestmentPortfolioController::visible()->findOrFail($row->dossier_id);
}
@@ -331,12 +337,14 @@ class InvestmentSubmissionController extends Controller
'sector_interest' => $p['sector'], 'message' => $p['objective'], 'privacy_consent' => true, 'consented_at' => now(),
'status' => 'received', 'source_page' => '/investidor/manifestacoes/'.$id,
'metadata' => ['submission_id' => $id, 'portal_user_id' => $request->user()->id, 'dossier_id' => $row->dossier_id,
'context_opportunity' => $p['context_opportunity'] ?? null,
'context_opportunity_snapshot' => $p['context_opportunity_snapshot'] ?? null,
'declared_origin_country_code' => $p['declared_origin_country_code'] ?? null,
'residence_country_code' => $p['residence_country_code'] ?? null,
'incorporation_country_code' => $p['incorporation_country_code'] ?? null,
...(isset($p['organization_country_snapshot']) ? ['organization_country_snapshot' => $p['organization_country_snapshot']] : []),
...($zoneOrigin ? ['zone_origin' => (array) $zoneOrigin] : [])]]));
$row->update(['payload' => $p, 'version' => $row->version + 1, 'interest_request_id' => $interest->id, 'status' => 'submitted']);
$row->update(['opportunity_id'=>$opportunity?->id, 'payload' => $p, 'version' => $row->version + 1, 'interest_request_id' => $interest->id, 'status' => 'submitted']);
InvestmentSpecificNeed::where('submission_id', $id)->delete();
foreach ($data['needs'] ?? [] as $need) {
InvestmentSpecificNeed::create([...$need, 'submission_id' => $id]);
@@ -11,6 +11,36 @@ use Inertia\Inertia;
final class InvestorContextController extends Controller
{
private function entryComplements(Request $request): array
{
$data = $request->validate(['complement_origin' => 'nullable|string|max:100', 'complement_code' => 'required_with:complement_origin|string|max:40',
'complement_target' => 'nullable|string|max:100', 'complement_territory' => 'nullable|uuid']);
if (empty($data['complement_origin'])) return [];
return app(\App\Services\InvestmentComplements::class)->resolve([['origin' => $data['complement_origin'], 'code' => $data['complement_code'],
'target' => $data['complement_target'] ?? null, 'territory_id' => $data['complement_territory'] ?? null]]);
}
private function entryChoices(Request $r, InvestorContext $context): array
{
$data = $r->validate(['source' => 'nullable|string|max:100', 'from_dossier' => 'nullable|uuid', 'opportunities' => 'sometimes|array|max:100', 'opportunities.*' => 'integer|distinct|min:1',
'province_id' => 'nullable|uuid', 'district_id' => 'nullable|uuid', 'admin_post_id' => 'nullable|uuid', 'locality_id' => 'nullable|uuid']);
$source = !empty($data['source']) ? $context->reference($data['source']) : null;
$ids = $data['opportunities'] ?? (($source['kind'] ?? '') === 'opportunity' ? [$source['id']] : []);
abort_if(!empty($data['from_dossier']) && !$ids, 422, 'Escolha a oportunidade associada à ficha da carteira.');
$path = app(InvestmentGeography::class)->selection($data);
$input = array_map(fn($id) => ['source' => 'opportunity:'.$id, ...($path ? ['territory' => 'territory:'.$path[array_key_last($path)]->id] : [])], $ids);
if (!empty($data['from_dossier'])) $input[0]['portfolio_origins'] = [$data['from_dossier']];
foreach ($this->entryComplements($r) as $complement) {
$keys = array_filter([$complement['origin']['key'], $complement['target']['key'] ?? null], fn($key) => str_starts_with($key, 'opportunity:'));
foreach ($keys as $key) {
if (in_array($key, array_column($input, 'source'), true)) continue;
$input[] = ['source' => $key, ...($complement['territory_id'] ? ['territory' => 'territory:'.$complement['territory_id']] : []),
...(str_starts_with($complement['origin']['key'], 'portfolio:') ? ['portfolio_origins' => [substr($complement['origin']['key'], 10)]] : [])];
}
}
return app(\App\Services\InvestmentOpportunityChoices::class)->resolve($input, [], true);
}
public function current(Request $r, InvestorContext $context)
{
return response()->json($context->current($r->user()));
@@ -23,7 +53,8 @@ final class InvestorContextController extends Controller
public function show(Request $r, string $id, InvestorContext $context)
{
return response()->json($context->view($r->user(), $id));
$data = $r->validate(['opportunity' => 'nullable|string|max:100']);
return response()->json($context->view($r->user(), $id, $data['opportunity'] ?? null));
}
public function options(Request $r, InvestorContext $context)
@@ -33,12 +64,56 @@ final class InvestorContextController extends Controller
return response()->json($context->options($d['q'] ?? ''));
}
public function corridorOptions(Request $r)
{
$data = $r->validate(['corridor' => ['nullable', \Illuminate\Validation\Rule::in(array_keys(config('investment_corridors')))], 'territory' => 'nullable|uuid']);
$service = app(\App\Services\InvestmentCorridorContext::class);
return response()->json(['corridors' => collect(config('investment_corridors'))->map(fn($entry,$key)=>['key'=>$key,'name'=>$entry['name'],'provinces'=>$service->provinces($key)])->values(),
'parks' => $service->parks($data['corridor'] ?? null, $data['territory'] ?? null)]);
}
public function saveDraft(Request $r, string $id, InvestorContext $context)
{
return response()->json($context->saveDraft($r->user(), $id, $r->all()));
}
public function edit(Request $r, string $id, InvestorContext $context)
{
$data = $context->view($r->user(), $id);
abort_unless($data['can_write'], 403);
return Inertia::render('investor-path/context', ['initial' => $data, 'options' => $context->options(), 'source' => $r->query('source') ? $context->reference($r->validate(['source' => 'required|string|max:100'])['source']) : null, 'territorySelection' => isset($data['context']['selection']['territory']) ? app(InvestmentGeography::class)->path($data['context']['selection']['territory']['id']) : []]);
$entries = $this->entryChoices($r, $context);
$choices = $entries ?: null;
$source = $r->query('source') ? $context->reference($r->validate(['source' => 'required|string|max:100'])['source']) : null;
if ($choices === null && ($source['kind'] ?? '') === 'opportunity') $choices = app(\App\Services\InvestmentOpportunityChoices::class)->resolve([['source' => $source['key']]]);
$explicitChoices=$choices;
$selectionKey=$r->validate(['selection_key'=>'nullable|uuid'])['selection_key']??null;
$entryKey=fn($c)=>$c['opportunity']['key'].($selectionKey?'@'.$selectionKey:'');
$entrySources=array_map($entryKey,$explicitChoices??[]);
if ($choices !== null) $choices = app(\App\Services\InvestmentOpportunityChoices::class)->mergeEntries($data['context']['opportunity_choices'] ?? [], $choices);
$draft = $context->draft($r->user(), $id);
if ($draft && isset($draft['opportunity_choices'])) {
$recovered = app(\App\Services\InvestmentOpportunityChoices::class)->resolve($draft['opportunity_choices'], $data['context']['opportunity_choices'] ?? [], true);
$new=array_filter($explicitChoices??[],fn($c)=>!in_array($entryKey($c),$draft['entry_sources']??[],true));
$choices = app(\App\Services\InvestmentOpportunityChoices::class)->mergeEntries($recovered, array_values($new));
}
$differences = [];
foreach ($explicitChoices ?? [] as $entry) {
$kept = collect($choices ?? [])->firstWhere('opportunity.key', $entry['opportunity']['key']);
if ($kept && ($kept['territory']['key'] ?? null) !== ($entry['territory']['key'] ?? null)) $differences[] = ['title'=>$entry['opportunity']['title'],'saved'=>$kept['territory']['title']??'Por definir','requested'=>$entry['territory']['title']??'Por definir'];
}
$complements = isset($draft['complements']) ? app(\App\Services\InvestmentComplements::class)->resolve($draft['complements'], $data['context']['complements'] ?? []) : ($data['context']['complements'] ?? []);
$entryTerritory = null;
if (($source['kind'] ?? null) === 'portfolio') {
$path = app(InvestmentGeography::class)->selection($r->validate(['province_id' => 'nullable|uuid', 'district_id' => 'nullable|uuid', 'admin_post_id' => 'nullable|uuid', 'locality_id' => 'nullable|uuid']));
if ($path) $entryTerritory = $context->reference('territory:'.$path[array_key_last($path)]->id);
}
foreach ($this->entryComplements($r) as $entry) {
$tag = 'complement:'.hash('sha256', $entry['key'].'@'.($selectionKey ?? ''));
$entrySources[] = $tag;
if (!in_array($tag, $draft['entry_sources'] ?? [], true)) $complements = collect([...$complements, $entry])->unique('key')->values()->all();
}
return Inertia::render('investor-path/context', ['entryTerritory' => $entryTerritory, 'entryComplements' => $complements, 'entryLocationDifferences'=>$differences,'entrySources'=>$entrySources,'draft' => $draft, 'requestedChoices' => $choices, 'initial' => $data, 'options' => $context->options(), 'source' => $source, 'territorySelection' => isset($data['context']['selection']['territory']) ? app(InvestmentGeography::class)->path($data['context']['selection']['territory']['id']) : ($entryTerritory ? app(InvestmentGeography::class)->path($entryTerritory['id']) : [])]);
}
public function preview(Request $r, string $id, InvestorContext $context)
@@ -55,8 +130,16 @@ final class InvestorContextController extends Controller
{
$d = $r->validate(['source' => 'nullable|string|max:100', 'step' => ['nullable', 'regex:/^S(0[1-9]|1[0-9])$/']]);
$ref = isset($d['source']) ? $context->reference($d['source']) : null;
$submissionId = $r->validate(['submission' => 'nullable|uuid'])['submission'] ?? null;
$entrySubmission = null;
if ($submissionId) {
$submission = \App\Models\InvestmentSubmission::where('id', $submissionId)->where('user_id', $r->user()->id)->where('status', 'draft')->firstOrFail();
abort_unless(($ref['kind'] ?? null) === 'portfolio' && $submission->dossier_id === $ref['id'], 422, 'Conserve a ficha de origem da manifestação.');
$entrySubmission = ['id' => $submission->id, 'version' => $submission->version];
}
return Inertia::render('investor-path/context-start', ['contexts' => $path->contexts($r->user()), 'source' => $ref, 'step' => $d['step'] ?? null]);
$choices = $this->entryChoices($r, $context);
return Inertia::render('investor-path/context-start', ['entrySubmission' => $entrySubmission, 'entryComplements' => $this->entryComplements($r), 'entryQuery' => $r->only(['complement_origin', 'complement_code', 'complement_target', 'complement_territory', 'from_dossier', 'province_id', 'district_id', 'admin_post_id', 'locality_id']), 'opportunityChoices' => $choices, 'contexts' => $path->contexts($r->user()), 'source' => $ref, 'step' => $d['step'] ?? null]);
}
public function installation(Request $r, InvestorContextInstallation $install)
@@ -88,7 +88,8 @@ class InvestorPathController extends Controller
public function show(Request $request, string $id, InvestorPath $path): JsonResponse|Response
{
$data = $path->view($request->user(), $id);
$choice = $request->validate(['opportunity' => 'nullable|string|max:100'])['opportunity'] ?? null;
$data = $path->view($request->user(), $id, $choice);
return $request->expectsJson() ? response()->json($data) : Inertia::render('investor-path/show', $data);
}
@@ -103,6 +103,7 @@ class OpportunityManagementController extends Controller
$records = $query->orderByDesc('id')->paginate(20)->withQueryString();
$records->getCollection()->each(function (Opportunity $r) use ($access, $service, $request) {
$r->setAttribute('can_edit', $access->canEdit($request->user(), $r));
$r->setAttribute('can_promote', app(\App\Services\OpportunityPromotion::class)->allowed($request->user(), $r));
$r->setAttribute('working_status', $access->state($r));
$r->setAttribute('publication', $r->publicationPresentation());
$r->setAttribute('draft_title', $service->values($r)['canonical']['official_title'] ?? $r->title);
@@ -130,6 +131,7 @@ class OpportunityManagementController extends Controller
'profileReauthenticationRequired' => $record && $record->owner_user_id !== null
? app(OpportunityProfileWorkflow::class)->reauthenticationRequired($request->user(), $record) : false,
'publicationState' => $record?->publicationPresentation(),
'promotionHref' => $record && app(\App\Services\OpportunityPromotion::class)->allowed($request->user(), $record) ? '/minhas-oportunidades/'.$record->id.'/divulgacao' : null,
'canonicalResponse' => $record?->owner_user_id !== null ? app(OpportunityCanonicalResponse::class)->read($request->user(), $record) : null,
'canEditDraft' => $record ? $access->canEdit($request->user(), $record) : false,
'workflowParticipants' => $record && $record->owner_user_id !== null ? app(OpportunityProfileWorkflow::class)->participants($request->user(), $record) : ['validators' => [], 'publishers' => []],
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers;
use App\Models\Opportunity;
use App\Services\OpportunityPromotion;
use Illuminate\Http\Request;
use Inertia\Inertia;
final class OpportunityPromotionController extends Controller
{
public function show(Request $request, int $id, OpportunityPromotion $service): \Inertia\Response
{
return Inertia::render('management/opportunity-promotion', $service->editor($request->user(), Opportunity::findOrFail($id)));
}
public function preview(Request $request, int $id, OpportunityPromotion $service): \Illuminate\Http\JsonResponse
{
return response()->json($service->preview($request->user(), Opportunity::findOrFail($id), $request->all()));
}
public function confirm(Request $request, int $id, OpportunityPromotion $service): \Illuminate\Http\JsonResponse
{
return response()->json($service->confirm($request->user(), $id, $request->all()));
}
}
@@ -9,6 +9,7 @@ use App\Services\CountryCatalogue;
use App\Services\IdempotencyConflict;
use App\Services\InterestSubmission;
use App\Services\LegacyContent;
use App\Services\PortalProfileAccess;
use App\Services\Malware\MalwareScanner;
use App\Services\VerifiedDocumentBinary;
use App\Services\VirtualDesk;
@@ -274,7 +275,7 @@ class PortalServicesController extends Controller
'resource' => $resource,
'backUrl' => $backUrl,
'downloadUrl' => route('services.download', ['id' => $id]),
'articleUrl' => Schema::hasColumn('portal_resources','editorial_content') && DB::table('portal_resources')->where('id',$id)->whereNotNull('editorial_content')->exists() ? '/artigos/'.$id : null,
'articleUrl' => $this->articleUrl($id),
]);
}
@@ -297,6 +298,7 @@ class PortalServicesController extends Controller
public function manage(Request $request, MalwareScanner $scanner): Response
{
$managementFilters = $request->validate(['q' => 'nullable|string|max:150', 'state' => 'nullable|in:draft,published,withdrawn', 'intent' => 'nullable|in:document,legislation']);
$access = app(\App\Services\DocumentManagementAccess::class);
if (! $access->full($request->user())) {
$documents = $access->documents($request->user())->where('status', '!=', 'deleted');
@@ -304,7 +306,7 @@ class PortalServicesController extends Controller
$health['signatures_at'] = $health['signatures_at']?->format(DATE_ATOM);
$editorial = $access->editorial($request->user());
$resources = $editorial ? $access->editorialResources($request->user())->orderByDesc('r.updated_at')
$resources = $editorial ? $this->filterManagedResources($access->editorialResources($request->user()), $managementFilters)->orderByDesc('r.updated_at')
->get(['r.*', 'd.file_name', 'd.media_type', 'd.size_bytes', 'd.issuer', 'd.status as file_status', 'd.data_classification', 'd.version_no as file_version', 'd.rights_status as file_rights_status', 'd.malware_scan_status', 'd.malware_failure_reason']) : collect();
$links = DB::table('portal_resource_links')->whereIn('resource_id', $resources->pluck('id'))->orderBy('id')->get()->groupBy('resource_id');
$revisions = DB::table('portal_resource_revisions')->whereIn('resource_id', $resources->pluck('id'))->orderByDesc('revision_no')->get(['resource_id', 'revision_no', 'change_type', 'actor_user_id', 'created_at'])->groupBy('resource_id');
@@ -313,6 +315,7 @@ class PortalServicesController extends Controller
$resource->revisions = $revisions->get($resource->id, collect())->values();
});
return Inertia::render('portal/manage', [
'managementFilters' => $managementFilters, 'associationOptions' => $this->contentAssociations($request),
'documentAccessOnly' => ! $editorial, 'editorialScopeOnly' => $editorial, 'resources' => $resources, 'partnerships' => [], 'territories' => [],
'requirementCatalogue' => [], 'requirementAssociationsEnabled' => false,
'disclosureAuthorization' => null, 'incompleteCount' => $resources->filter(fn ($r) => ! $r->description || ! $r->issuer || ! $r->source_url || $r->source_confirmation === 'unconfirmed' || $r->legal_status === 'unknown' || $r->rights_status === 'unconfirmed')->count(), 'possibleDuplicates' => $resources->whereNotNull('duplicate_of_resource_id')->count(),
@@ -331,7 +334,7 @@ class PortalServicesController extends Controller
->where('external_id', 'document-repository-disclosure-authorization-2026-10-06-v1')
->where('action', 'public_disclosure_authorized')->first(['external_id', 'occurred_at', 'after_data']);
$disclosureAuthorizationData = $disclosureAuthorization ? json_decode($disclosureAuthorization->after_data, true) : null;
$resources = DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')->orderByDesc('r.updated_at')->limit(200)
$resources = $this->filterManagedResources(DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')->whereNull('d.deleted_at'), $managementFilters)->orderByDesc('r.updated_at')->limit(200)
->get(['r.*', 'd.file_name', 'd.media_type', 'd.size_bytes', 'd.issuer', 'd.status as file_status', 'd.data_classification', 'd.version_no as file_version', 'd.rights_status as file_rights_status', 'd.malware_scan_status', 'd.malware_failure_reason']);
$links = DB::table('portal_resource_links')->whereIn('resource_id', $resources->pluck('id'))->orderBy('id')->get()->groupBy('resource_id');
$resources->each(fn ($resource) => $resource->links = $links->get($resource->id, collect())->values());
@@ -342,6 +345,7 @@ class PortalServicesController extends Controller
$malwareHealth['signatures_at'] = $malwareHealth['signatures_at']?->format(DATE_ATOM);
return Inertia::render('portal/manage', ['resources' => $resources,
'managementFilters' => $managementFilters, 'associationOptions' => $this->contentAssociations($request),
'requirementCatalogue' => $this->requirementEvidenceEnabled()
? collect(is_array($catalogue = config('document-requirements.catalogue', [])) ? $catalogue : [])->map(fn ($item, $key) => ['key' => $key, ...$item])->values()
: collect(),
@@ -367,6 +371,39 @@ class PortalServicesController extends Controller
'possibleDuplicates' => DB::table('portal_resources')->whereNotNull('duplicate_of_resource_id')->count()]);
}
private function filterManagedResources(Builder $query, array $filters): Builder
{
if (! empty($filters['q'])) {
$term = '%'.addcslashes($filters['q'], '%_\\').'%';
$query->where(fn ($q) => $q->where('r.title', 'ilike', $term)->orWhere('r.document_number', 'ilike', $term)->orWhere('d.issuer', 'ilike', $term)->orWhere('d.file_name', 'ilike', $term));
}
if (! empty($filters['state'])) {
$query->where('r.published', $filters['state'] === 'published');
if ($filters['state'] !== 'published') {
$method = $filters['state'] === 'withdrawn' ? 'whereExists' : 'whereNotExists';
$query->$method(fn ($q) => $q->selectRaw('1')->from('portal_resource_revisions as history')->whereColumn('history.resource_id', 'r.id')->whereIn('history.change_type', ['unpublished', 'withdrawn']));
}
}
return $query;
}
private function contentAssociations(Request $request): array
{
$access = app(\App\Services\DocumentManagementAccess::class);
$resources = $access->full($request->user()) ? DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')->whereNull('d.deleted_at')
: ($access->editorial($request->user()) ? $access->editorialResources($request->user()) : null);
$documents = $resources ? $resources->orderBy('r.title')->get(['r.id as value', 'r.title as label', 'r.document_type']) : collect();
return [
'sector' => \App\Models\Sector::choices()->orderBy('name')->get(['id as value', 'name as label']),
'territory' => app(\App\Services\InvestmentGeography::class)->publicReferences()->orderBy('name')->get(['id as value', 'name as label']),
'opportunity' => app(PortalProfileAccess::class)->scope(\App\Models\Opportunity::query(), $request->user())->orderBy('title')->get(['id as value', 'title as label']),
'document' => $documents,
'procedure' => $documents->where('document_type', 'procedure')->values(),
'stage' => collect(config('virtual-desk.steps', []))->map(fn ($step, $key) => ['value' => $key, 'label' => $step['title']])->values(),
'activity' => app(\App\Services\EconomicReferenceCatalog::class)->activities()->map(fn ($row) => ['value' => $row->id, 'label' => $row->display_label])->values(),
];
}
public function upload(Request $request): RedirectResponse
{
$this->validateManagement($request, ['file' => ['required', 'file', 'mimetypes:application/pdf,text/plain,text/csv,image/jpeg,image/png,image/webp,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.text'.(app(LegacyContent::class)->available() ? ',application/json' : ''), 'max:10240'],
@@ -399,7 +436,7 @@ class PortalServicesController extends Controller
ScanDocumentForMalware::dispatch($id)->afterCommit();
return to_route('services.manage')->with('success', 'Documento recebido em armazenamento privado e quarentena. Não aparece na biblioteca pública até validação e publicação explícitas.');
return to_route('services.manage', array_filter(['intent' => in_array($request->input('intent'), ['document', 'legislation'], true) ? $request->input('intent') : null]))->with('uploaded_document_id', $id)->with('success', 'Documento recebido em armazenamento privado e quarentena. Não aparece na biblioteca pública até validação e publicação explícitas.');
}
public function resource(Request $request): RedirectResponse
@@ -663,7 +700,7 @@ class PortalServicesController extends Controller
'data_classification' => ['nullable', 'in:internal,public'],
'source_reference' => ['required', 'string', 'min:10', 'max:2000'],
'legislation' => ['required', 'boolean'], 'essential' => ['required', 'boolean'],
'associations' => ['nullable', 'array', 'max:40'], 'associations.*.target_type' => ['required', 'in:theme,module,activity,stage,procedure,entity,territory,document,investment_type,legal_source'.($this->requirementEvidenceEnabled() ? ',requirement' : '')],
'associations' => ['nullable', 'array', 'max:40'], 'associations.*.target_type' => ['required', 'in:theme,module,activity,stage,procedure,entity,territory,document,investment_type,legal_source,sector,opportunity'.($this->requirementEvidenceEnabled() ? ',requirement' : '')],
'associations.*.target_key' => ['required', 'string', 'max:180'],
'associations.*.review_status' => ['nullable', 'in:pending,confirmed'],
'associations.*.relationship' => ['required', 'in:applies_to,supports,version_of,translation_of,amends,repeals,implements,cites,related_to'],
@@ -829,4 +866,17 @@ SQL, [$id]);
return $ids ?: [$id];
}
private function articleUrl(int $id): ?string
{
if (!Schema::hasColumn('portal_resources', 'editorial_content')) return null;
$resource = DB::table('portal_resources')->find($id);
if (!$resource) return null;
if ($resource->editorial_content !== null) return '/artigos/'.$id;
try {
return app(\App\Services\EditorialReading::class)->original($resource) ? '/artigos/'.$resource->id : null;
} catch (\Symfony\Component\HttpKernel\Exception\HttpException) {
return null;
}
}
}
@@ -0,0 +1,138 @@
<?php
namespace App\Http\Controllers;
use App\Services\Procurement;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
class ProcurementController extends Controller
{
private const STATE_SQL = "CASE WHEN v.payload->>'procedural_state' IN ('cancelled','results') THEN v.payload->>'procedural_state' WHEN ((v.payload->>'deadline')::timestamp AT TIME ZONE (v.payload->>'timezone'))<=CURRENT_TIMESTAMP THEN 'deadline_ended' WHEN v.payload->>'extended'='true' THEN 'extended' ELSE 'open' END";
private function query(Procurement $service)
{
abort_unless($service->available(), 404);
return DB::table('portal_tenders as t')->join('portal_tender_versions as v', fn ($q) => $q->on('v.id', '=', 't.published_version_id')->on('v.tender_id', '=', 't.id'))->whereNotNull('v.published_at');
}
public function index(Request $request, Procurement $service)
{
$filters = $request->validate(['q' => 'nullable|string|max:150', 'entity' => 'nullable|uuid', 'sector' => 'nullable|integer', 'territory' => 'nullable|uuid', 'modality' => 'nullable|string|max:200', 'financier' => 'nullable|string|max:200', 'state' => 'nullable|in:open,extended,deadline_ended,cancelled,results', 'deadline_before' => 'nullable|date_format:Y-m-d', 'regime' => 'nullable|in:state,external,private,other']);
$filters = array_filter($filters, fn ($value) => $value !== null && $value !== '');
$query = $this->query($service);
if (! empty($filters['q'])) {
$query->where(fn ($q) => $q->whereRaw("v.payload->>'title' ILIKE ?", ['%'.$filters['q'].'%'])->orWhereRaw("v.payload->>'object' ILIKE ?", ['%'.$filters['q'].'%'])->orWhereRaw("v.payload->>'reference' ILIKE ?", ['%'.$filters['q'].'%']));
}
foreach (['entity' => 'organization_id', 'sector' => 'sector_id', 'modality' => 'modality', 'financier' => 'financier', 'regime' => 'regime'] as $filter => $field) {
if (! empty($filters[$filter])) {
$query->whereRaw('v.payload->>? = ?', [$field, (string) $filters[$filter]]);
}
}
if (! empty($filters['territory'])) {
$query->whereRaw("v.payload->>'territory_id' IN (WITH RECURSIVE scope(id,depth) AS (SELECT id,0 FROM geo_locations WHERE id=? AND deleted_at IS NULL UNION ALL SELECT c.id,s.depth+1 FROM geo_locations c JOIN scope s ON c.parent_id=s.id WHERE c.deleted_at IS NULL AND s.depth<4) SELECT id::text FROM scope)", [$filters['territory']]);
}
if (! empty($filters['state'])) {
$query->whereRaw(self::STATE_SQL.' = ?', [$filters['state']]);
}
if (! empty($filters['deadline_before'])) {
$query->whereRaw("left(v.payload->>'deadline',10)<=?", [$filters['deadline_before']]);
}
$rows = $query->select('t.id', 'v.id as vid')->orderByDesc('v.published_at')->paginate(20)->withQueryString();
$rows->through(fn ($r) => ['id' => $r->id, ...$this->summary($service, $r->vid)]);
$facets = $this->query($service)->get(['v.payload'])->map(fn ($r) => json_decode($r->payload, true));
$options = [];
foreach (['entity' => ['organization_id', 'organization_name'], 'sector' => ['sector_id', 'sector_name'], 'territory' => ['territory_id', 'territory_name'], 'modality' => ['modality', 'modality'], 'financier' => ['financier', 'financier']] as $name => [$value,$label]) {
$options[$name] = $facets->filter(fn ($p) => ! empty($p[$value]))->map(fn ($p) => ['id' => (string) $p[$value], 'name' => $p[$label]])->unique('id')->values();
}
return Inertia::render('procurement/index', ['items' => $rows, 'filters' => $filters, 'options' => $options, 'regimes' => config('procurement.regimes'), 'management' => $service->access($request->user())])->toResponse($request)->header('Cache-Control', 'private, no-store');
}
private function summary(Procurement $service, string $vid): array
{
$v = $service->publishedVersion($vid);
return ['publication' => $v, 'state' => $service->state($v)];
}
public function show(Request $request, string $id, Procurement $service)
{
return $this->detail($request, $id, $service, false);
}
public function print(Request $request, string $id, Procurement $service)
{
return $this->detail($request, $id, $service, true);
}
private function detail(Request $request, string $id, Procurement $service, bool $print)
{
$row = $this->query($service)->where('t.id', $id)->firstOrFail(['t.id', 'v.id as vid']);
$filters = $request->only(['q', 'entity', 'sector', 'territory', 'modality', 'financier', 'state', 'deadline_before', 'regime', 'page']);
return Inertia::render('procurement/show', ['id' => $id, ...$this->summary($service, $row->vid), 'printMode' => $print, 'backUrl' => '/concursos'.($filters ? '?'.http_build_query($filters) : ''),
'history' => DB::table('portal_tender_versions')->where('tender_id', $id)->whereNotNull('published_at')->orderBy('version')->pluck('id')->map(fn ($vid) => $service->publishedVersion($vid)),
'watching' => $request->user() && DB::table('portal_tender_watches')->where('tender_id', $id)->where('user_id', $request->user()->id)->exists()])->toResponse($request)->header('Cache-Control', 'private, no-store');
}
public function manage(Request $request, Procurement $service)
{
$service->guard($request->user());
return Inertia::render('procurement/manage', ['items' => DB::table('portal_tenders')->orderByDesc('updated_at')->get(['id', 'draft', 'version', 'published_version_id'])->map(fn ($r) => ['id' => $r->id, 'title' => json_decode($r->draft, true)['title'] ?? 'Rascunho sem título', 'version' => $r->version, 'published' => (bool) $r->published_version_id])])->toResponse($request)->header('Cache-Control', 'private, no-store');
}
public function edit(Request $request, Procurement $service, ?string $id = null)
{
$service->guard($request->user());
$r = $id ? $service->record($id) : null;
return Inertia::render('procurement/edit', ['record' => $r ? ['id' => $id, 'version' => $r->version, 'draft' => json_decode($r->draft, true), 'published' => (bool) $r->published_version_id] : null,
'references' => $service->references($request->user()), 'history' => $id ? DB::table('portal_tender_versions')->where('tender_id', $id)->orderByDesc('version')->get(['version', 'act', 'reason', 'actor_id', 'published_at', 'created_at']) : []])->toResponse($request)->header('Cache-Control', 'private, no-store');
}
public function save(Request $request, Procurement $service, ?string $id = null)
{
$id = $service->save($request->user(), $id, $request->all());
return redirect('/management/concursos/'.$id)->with('success', 'Rascunho guardado. A publicação anterior permanece intacta.');
}
public function preview(Request $request, string $id, Procurement $service)
{
return response()->json($service->preview($request->user(), $id, (string) $request->input('act')))->header('Cache-Control', 'private, no-store');
}
public function publish(Request $request, string $id, Procurement $service)
{
$service->publish($request->user(), $id, $request->all());
return redirect('/management/concursos/'.$id)->with('success', 'Acto publicado, com versão e auditoria. Nenhuma proposta recebida pelo portal.');
}
public function watch(Request $request, string $id, Procurement $service)
{
$row = $this->query($service)->where('t.id', $id)->firstOrFail(['v.id']);
DB::table('portal_tender_watches')->updateOrInsert(['tender_id' => $id, 'user_id' => $request->user()->id], ['seen_version_id' => $row->id, 'created_at' => now(), 'updated_at' => now()]);
return back()->with('success', 'Concurso guardado para acompanhamento. Isto não apresenta uma proposta.');
}
public function unwatch(Request $request, string $id, Procurement $service)
{
abort_unless($service->available(), 404);
DB::table('portal_tender_watches')->where('tender_id', $id)->where('user_id', $request->user()->id)->delete();
return back();
}
public function watched(Request $request, Procurement $service)
{
$rows = $this->query($service)->join('portal_tender_watches as w', 'w.tender_id', '=', 't.id')->where('w.user_id', $request->user()->id)->get(['t.id', 'v.id as vid', 'w.seen_version_id'])->map(fn ($r) => ['id' => $r->id, 'changed' => $r->seen_version_id !== $r->vid, ...$this->summary($service, $r->vid)]);
return Inertia::render('procurement/manage', ['watched' => true, 'items' => $rows])->toResponse($request)->header('Cache-Control', 'private, no-store');
}
}
@@ -11,8 +11,13 @@ class PublicGeographyController extends Controller
{
public function options(Request $request, InvestmentGeography $geography): JsonResponse
{
$data = $request->validate(['type' => ['required', 'in:province,district,admin_post,locality'], 'id' => ['nullable', 'uuid'], 'parent_id' => ['nullable', 'uuid'], 'q' => ['nullable', 'string', 'max:100'], 'page' => ['nullable', 'integer', 'min:1']]);
$data = $request->validate(['corridor' => ['nullable', \Illuminate\Validation\Rule::in(array_keys(config('investment_corridors')))], 'type' => ['required', 'in:province,district,admin_post,locality'], 'id' => ['nullable', 'uuid'], 'parent_id' => ['nullable', 'uuid'], 'q' => ['nullable', 'string', 'max:100'], 'page' => ['nullable', 'integer', 'min:1']]);
$query = $geography->publicReferences()->where('location_type', $data['type']);
if (!empty($data['corridor'])) {
$corridors = app(\App\Services\InvestmentCorridorContext::class);
if ($data['type'] === 'province') $query->whereIn('id', array_column($corridors->provinces($data['corridor']), 'id'));
elseif (!$corridors->compatible($data['corridor'], $data['parent_id'] ?? null)) abort(422, 'O território não pertence ao corredor escolhido.');
}
if (! empty($data['id'])) {
$query->where('id', $data['id']);
}
@@ -29,6 +34,6 @@ class PublicGeographyController extends Controller
->orWhereRaw('source_code ILIKE ?', [$term]));
}
return response()->json($query->orderBy('name')->orderBy('id')->paginate(100, ['id', 'name', 'parent_id', 'location_type', 'source_code']));
return response()->json($query->orderBy('name')->orderBy('id')->paginate(100, ['id', 'name', 'parent_id', 'location_type', 'source_code'])->withQueryString());
}
}
@@ -119,8 +119,8 @@ class VirtualDeskController extends Controller
public function createRequest(Request $request, string $id, VirtualDesk $desk): JsonResponse|RedirectResponse
{
$data = $request->validate(['service_version_id' => 'required|uuid', 'client_key' => 'required|uuid']);
$row = $desk->createRequest($request->user(), $id, $data['service_version_id'], $data['client_key']);
$data = $request->validate(['service_version_id' => 'required|uuid', 'client_key' => 'required|uuid', 'opportunity' => 'nullable|string|max:100']);
$row = $desk->createRequest($request->user(), $id, $data['service_version_id'], $data['client_key'], $data['opportunity'] ?? null);
return $request->expectsJson() ? response()->json(['id' => $row->id, 'version' => $row->version], 201) : redirect('/balcao-virtual/pedidos/'.$row->id);
}
@@ -14,7 +14,7 @@ class EnsureScopedEditorial
return $next($request);
}
abort_unless($access->editorial($actor), 403);
$allowed = in_array($request->route()->getName(), ['editorial.edit', 'editorial.save', 'services.resource', 'services.resource.update'], true)
$allowed = in_array($request->route()->getName(), ['editorial.edit', 'editorial.save', 'editorial.preview', 'editorial.preview-content', 'services.resource', 'services.resource.update'], true)
|| ($request->route()->getName() === 'services.moderate' && $request->route('type') === 'resources');
abort_unless($allowed, 403);
if ($request->route('id') !== null) {
@@ -19,7 +19,8 @@ final class EnsureScopedPortalAdministration
$path = $request->path();
$interests = $request->is('management/interests', 'management/interests/*', 'api/v1/investment/admin/interests/*');
// Fiscal maintenance reuses authorised administration; unrelated gates remain unchanged.
$catalogue = $request->is('management/economic-references/lot-01', 'management/economic-references/lot-01/*',
$catalogue = $request->is('management/territory-geometry-import', 'management/territory-geometry-import/*',
'management/economic-references/lot-01', 'management/economic-references/lot-01/*',
'management/education', 'management/education/*', 'management/investment/references', 'management/investment/references/*', 'api/v1/investment/admin/references', 'api/v1/investment/admin/references/*',
'management/investment/zones', 'management/investment/zones/*', 'api/v2/investment/admin/zones', 'api/v2/investment/admin/zones/*',
'management/balcao/atribuicoes', 'management/balcao/atribuicoes/*', 'management/balcao/servicos',
@@ -51,8 +51,10 @@ class HandleInertiaRequests extends Middleware
return [
...parent::share($request),
'name' => config('app.name'),
'procurementEnabled' => app(\App\Services\Procurement::class)->available(),
'homologationReserved' => (bool) config('portal.homologation_only', false),
'profileAccess' => app(PortalProfileAccess::class)->navigation($request->user()),
'contentManagementHref' => app(\App\Services\ContentManagement::class)->access($request->user()) !== [] ? '/gestao-conteudos' : null,
'portalOperator' => config('portal.operator_name'),
'portalInstitutionalContactEmail' => filter_var(config('portal.institutional_contact_email'), FILTER_VALIDATE_EMAIL) ?: null,
'legacyContentEnabled' => app(LegacyContent::class)->available(),
@@ -68,7 +70,7 @@ class HandleInertiaRequests extends Middleware
'create' => ['label' => 'Nova oportunidade', 'href' => '/management/opportunities/create'],
'installationHref' => '/management/opportunity-management-install',
] : null,
'flash' => ['success' => fn () => $request->session()->get('success'), 'warning' => fn () => $request->session()->get('warning')],
'flash' => ['success' => fn () => $request->session()->get('success'), 'warning' => fn () => $request->session()->get('warning'), 'uploaded_document_id' => fn () => $request->session()->get('uploaded_document_id')],
'scopedAdministration' => $request->user() && app(\App\Services\ScopedPortalAdministration::class)->administrative($request->user()) ? [
['label'=>'Zonas e parques','href'=>'/management/investment/zones'],
['label'=>'Referências económicas','href'=>'/management/economic-references/lot-01'],
@@ -29,7 +29,9 @@ class ProtectProfileOpportunity
if ($record && ($record->owner_user_id !== null || str_starts_with($request->path(), 'minhas-oportunidades'))) {
abort_if($record->owner_user_id === null && str_starts_with($request->path(), 'minhas-oportunidades'), 403, 'Registo legado não convertido para a política v1.');
$access = app(PortalProfileAccess::class);
$access->authorize($request->user(), $request->isMethod('GET') || str_ends_with($request->path(), '/workflow') || str_ends_with($request->path(), '/preview') || $request->is('minhas-oportunidades/*/classification/review') ? 'view' : 'edit', $record);
// The promotional controller checks its own scoped disclosure capability,
// like workflow decisions; publication does not grant draft editing.
$access->authorize($request->user(), $request->isMethod('GET') || str_ends_with($request->path(), '/workflow') || str_ends_with($request->path(), '/preview') || $request->is('minhas-oportunidades/*/classification/review', 'minhas-oportunidades/*/divulgacao/previa', 'minhas-oportunidades/*/divulgacao/confirmar') ? 'view' : 'edit', $record);
abort_if($request->is('management/opportunity-origin/apply'), 403, 'O cadastro privado v1 não usa a classificação editorial dos exemplos legados.');
abort_if($request->isMethod('POST') && str_ends_with($request->path(), '/lifecycle') && ! str_contains($request->path(), '/resources/'), 403, 'Use o workflow com decisões segregadas; o perfil não concede exclusão.');
}
@@ -19,7 +19,9 @@ class StoreLandAssistanceCaseRequest extends FormRequest
{
$payload = $this->validated('payload');
return is_array($payload) ? $payload : [];
if (!is_array($payload)) return [];
if (!empty($payload['investor_path_case_id'])) return app(\App\Services\InvestmentConsumerScope::class)->bind($this->user(), $payload['investor_path_case_id'], $payload, $this->routeIs('land-assistance.support'));
return \Illuminate\Support\Arr::except($payload, ['context_opportunity', 'context_opportunity_snapshot']);
}
public function authorize(): bool
@@ -33,7 +35,9 @@ class StoreLandAssistanceCaseRequest extends FormRequest
return [
'version' => [$this->isMethod('post') && ! $this->routeIs('land-assistance.support') ? 'nullable' : 'required', 'integer', 'min:1'],
'context_revision' => ['nullable', 'integer', 'min:0'], 'context_reviewed' => ['nullable', 'boolean'],
'payload' => ['required', 'array:context_reuse,support_kind,request_description,project_reference,investor_path_case_id,applicant_category,full_name,contact_person,email,phone,preferred_contact,country,declared_origin_country_code,nationality,representative_name,representative_role,representation_basis,nuit,constitution_or_residency_note,area_status,parcels,purpose,activity,known_occupants,occupant_description,known_improvements,improvements_description,prior_right_status,prior_right_description,conflict_status,conflict_description,official_process,attachments_to_follow,exploration_plan,consultation,additional_information,truth_declaration,contact_consent'],
'payload' => ['required', 'array:context_opportunity,context_opportunity_snapshot,context_reuse,support_kind,request_description,project_reference,investor_path_case_id,applicant_category,full_name,contact_person,email,phone,preferred_contact,country,declared_origin_country_code,nationality,representative_name,representative_role,representation_basis,nuit,constitution_or_residency_note,area_status,parcels,purpose,activity,known_occupants,occupant_description,known_improvements,improvements_description,prior_right_status,prior_right_description,conflict_status,conflict_description,official_process,attachments_to_follow,exploration_plan,consultation,additional_information,truth_declaration,contact_consent'],
'payload.context_opportunity' => ['nullable', 'string', 'max:100'],
'payload.context_opportunity_snapshot' => ['nullable', 'string', 'max:200000'],
'payload.context_reuse' => ['nullable', 'string', 'max:10000'],
'payload.support_kind' => ['nullable', 'in:investment,home,family_exploitation,recognition,regularization,delimitation,change_of_use,transfer,existing_process,conflict_guidance,other'],
'payload.request_description' => ['nullable', 'string', 'max:4000'],
@@ -37,7 +37,7 @@ class StoreSimulationFormDraftRequest extends FormRequest
'contact_province', 'contact_district', 'preferred_channel',
];
$economic = [
'context_applicability_snapshot', 'investment_regime', 'context_reuse', 'compatible_fields_snapshot', 'published_iva_base', 'published_iva_currency', 'published_iva_date', 'published_iva_scope', 'monthly_iva_settings', 'monthly_iva_snapshot', 'journey_type', 'detail_level', 'business_plan_snapshot', 'financial_tools_snapshot', 'cashflow_snapshot', 'project_team_snapshot', 'project_execution_snapshot', 'purpose', 'manifestation_type', 'preferred_language', 'project_title', 'project_summary', 'project_problem', 'target_customers', 'value_proposition', 'market_assumption', 'market_source', 'project_milestones', 'selection_labels', 'project_type',
'context_opportunity', 'context_applicability_snapshot', 'investment_regime', 'context_reuse', 'compatible_fields_snapshot', 'published_iva_base', 'published_iva_currency', 'published_iva_date', 'published_iva_scope', 'monthly_iva_settings', 'monthly_iva_snapshot', 'journey_type', 'detail_level', 'business_plan_snapshot', 'financial_tools_snapshot', 'cashflow_snapshot', 'project_team_snapshot', 'project_execution_snapshot', 'purpose', 'manifestation_type', 'preferred_language', 'project_title', 'project_summary', 'project_problem', 'target_customers', 'value_proposition', 'market_assumption', 'market_source', 'project_milestones', 'selection_labels', 'project_type',
'competitor_assumption', 'sales_channel_assumption', 'marketing_approach', 'location_rationale', 'project_risk_responses_snapshot', 'project_questionnaire_version',
'customer_validation_status', 'cost_estimate_status', 'location_assessment_status', 'funding_status', 'receivables_timing_status',
'project_type_other', 'sector_id', 'activity_id', 'products_services', 'maturity_stage', 'expected_start_date',
@@ -72,7 +72,8 @@ class StoreSimulationFormDraftRequest extends FormRequest
'economic_snapshot.business_plan_snapshot' => ['nullable', 'string', 'max:60000'],
'economic_snapshot.financial_tools_snapshot' => ['nullable', 'string', 'max:30000'],
'economic_snapshot.cashflow_snapshot' => ['nullable', 'string', 'max:20000'],
'economic_snapshot.context_applicability_snapshot' => ['nullable', 'string', 'max:20000'],
'economic_snapshot.context_applicability_snapshot' => ['nullable', 'string', 'max:1000000'],
'economic_snapshot.context_opportunity' => ['nullable', 'string', 'max:100'],
'economic_snapshot.investment_regime' => ['nullable', 'in:general,industrial_park,zee,zfi'],
'economic_snapshot.context_reuse' => ['nullable', 'string', 'max:10000'],
'economic_snapshot.compatible_fields_snapshot' => ['nullable', 'string', 'max:10000'],
+9 -1
View File
@@ -31,6 +31,13 @@ class Opportunity extends Model
protected $appends = ['maturity_catalogue_version'];
public function toArray(): array
{
$data = parent::toArray();
if (isset($data['description'])) $data['description'] = \App\Services\OpportunityPublicText::description($this, $data['description']);
return $data;
}
public function getMaturityCatalogueVersionAttribute(): ?string
{
// Only published metadata is public; never expose the current private draft.
@@ -68,7 +75,8 @@ class Opportunity extends Model
$scope = $public ? 'public' : ($visible && $this->owner_user_id !== null ? 'restricted' : 'unpublished');
return ['scope' => $scope, 'public_catalog' => $public, 'label' => match ($scope) {
'public' => 'Publicada no catálogo público',
'public' => data_get($this->workflow_context, 'publication.purpose') === 'promotional_preparation'
? 'Resumo divulgado — Em preparação; não autoriza execução' : 'Publicada no catálogo público',
'restricted' => 'Disponibilizada com acesso restrito — perfis e organização autorizados',
default => 'Sem versão disponibilizada; aprovação interna é uma etapa distinta',
}];
+106
View File
@@ -0,0 +1,106 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\DB;
use RuntimeException;
/** Exact replay of the 28 accepted links into the transported project identities. */
final class ApprovedAnchorTerritories
{
public const HASH = 'd2893d0a638d630435aca1890f79a8094dc58eb65bbdd65a470bb685fa075190';
/** @return array{manifest_sha256:string,projects:list<array<string,mixed>>,links:list<array<string,mixed>>} */
public function plan(): array
{
$file = base_path('docs/investmoz/unification/approved-anchor-territories-20261023.json');
$contents = is_file($file) ? file_get_contents($file) : false;
if ($contents !== false) {
$contents = str_replace("\r\n", "\n", $contents);
}
if ($contents === false || ! hash_equals(self::HASH, hash('sha256', $contents))) {
throw new RuntimeException('Reviewed territorial manifest changed.');
}
$manifest = json_decode($contents, true, 512, JSON_THROW_ON_ERROR);
if (count($manifest['items']) !== 28) {
throw new RuntimeException('Exact 28-link scope required.');
}
$projects = [];
$links = [];
foreach ($manifest['items'] as $item) {
$project = DB::table('projects')->where('id', $item['project_id'])->whereNull('deleted_at')->first();
$dossier = DB::table('investment_dossiers')->where('id', $item['dossier_id'])->whereNull('deleted_at')->first();
if (! $project || $project->code !== $item['code'] || ! $dossier || $dossier->project_id !== $project->id
|| ! hash_equals($item['content_sha256'], hash('sha256', $dossier->content))
|| ! hash_equals($item['source_sha256'], hash('sha256', $dossier->source))) {
throw new RuntimeException('Project identity or documentary source changed: '.$item['code']);
}
$original = $item['original_relation'];
$geo = DB::table('geo_locations')->where('id', $original['geo_location_id'])->whereNull('deleted_at')->first();
foreach (['location_type', 'source_code', 'parent_id', 'data_classification'] as $field) {
if (! $geo || $item['territory'][$field] !== $geo->$field) {
throw new RuntimeException('Territory identity changed: '.$original['geo_location_id']);
}
}
$metadata = $projects[$project->id]['after_metadata'] ?? json_decode($project->metadata, true, 512, JSON_THROW_ON_ERROR);
$key = $geo->id.':'.$original['role'];
$evidence = $item['evidence'];
// Preserve the original dossier id in evidence and explicitly map the destination.
$evidence['transported_dossier_id'] = $dossier->id;
if (isset($metadata['documented_location_sources'][$key]) && $metadata['documented_location_sources'][$key] != $evidence) {
throw new RuntimeException('Existing territorial evidence differs; no overwrite: '.$item['code']);
}
$projects[$project->id] ??= ['id' => $project->id, 'code' => $project->code, 'before_version' => (int) $project->version_no, 'before_metadata' => json_decode($project->metadata, true, 512, JSON_THROW_ON_ERROR)];
$metadata['documented_location_sources'][$key] = $evidence;
$projects[$project->id]['after_metadata'] = $metadata;
$existing = DB::table('project_locations')->where('project_id', $project->id)->where('geo_location_id', $geo->id)->where('role', $original['role'])->first();
if ($existing && ($existing->deleted_at || $existing->data_classification !== 'public')) {
throw new RuntimeException('Archived or restricted territorial relation must not be revived.');
}
$identity = DB::table('project_locations')->where('id', $original['id'])->first();
if ($identity && (! $existing || $identity->id !== $existing->id)) {
throw new RuntimeException('Historical relation identity is already occupied.');
}
$links[] = ['code' => $item['code'], 'already_present' => (bool) $existing, 'row' => [...$original, 'project_id' => $project->id]];
}
return ['manifest_sha256' => self::HASH, 'projects' => array_values($projects), 'links' => $links];
}
/** Caller supplies an authenticated, backed-up transaction; no source publication changes.
* @param array{manifest_sha256:string,projects:list<array<string,mixed>>,links:list<array<string,mixed>>} $plan
* @return array<string,int>
*/
public function apply(array $plan): array
{
if (DB::transactionLevel() < 1 || $this->plan() !== $plan) {
throw new RuntimeException('Reviewed transaction and unchanged preview required.');
}
$inserted = 0;
$updated = 0;
foreach ($plan['projects'] as $project) {
if ($project['before_metadata'] == $project['after_metadata']) {
continue;
}
$count = DB::table('projects')->where('id', $project['id'])->where('version_no', $project['before_version'])->update([
'metadata' => json_encode($project['after_metadata'], JSON_THROW_ON_ERROR), 'version_no' => $project['before_version'] + 1, 'updated_at' => now(),
]);
if ($count !== 1) {
throw new RuntimeException('Concurrent project change.');
}
DB::table('audit_events')->insert(['source_system' => 'approved-anchor-territories-recovery-v1', 'external_id' => self::HASH.':'.$project['id'], 'entity_type' => 'projects', 'entity_id' => $project['id'], 'action' => 'accepted_territories_restored',
'data_classification' => 'public', 'before_data' => json_encode(['version_no' => $project['before_version'], 'metadata' => $project['before_metadata']], JSON_THROW_ON_ERROR),
'after_data' => json_encode(['version_no' => $project['before_version'] + 1, 'manifest_sha256' => self::HASH, 'metadata' => $project['after_metadata'], 'new_approval' => false], JSON_THROW_ON_ERROR)]);
$updated++;
}
foreach ($plan['links'] as $link) {
if ($link['already_present']) {
continue;
}
DB::table('project_locations')->insert($link['row']);
$inserted++;
}
return ['relations_restored' => $inserted, 'projects_updated' => $updated, 'new_dossiers' => 0, 'publication_changes' => 0];
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace App\Services;
use App\Models\Opportunity;
use App\Models\User;
use Illuminate\Support\Facades\DB;
/** Navigation over existing authorisations; never grants a role or publishes content. */
final class ContentManagement
{
public function access(?User $actor): array
{
if (! $actor || ! $actor->hasVerifiedEmail()) return [];
$profiles = app(PortalProfileAccess::class);
$navigation = $profiles->navigation($actor);
$legacy = $profiles->legacyWriteAllowed($actor);
$documents = app(DocumentManagementAccess::class);
$editor = $documents->full($actor) || $documents->editorial($actor);
$upload = $editor || $documents->limited($actor);
$result = [];
if (app(Procurement::class)->available() && app(Procurement::class)->access($actor)) $result['procurement'] = ['index' => '/management/concursos', 'create' => '/management/concursos/novo'];
if ($legacy || ($navigation['mine'] ?? null) || ($navigation['review'] ?? null)) {
$result['opportunities'] = ['index' => $legacy ? '/management/opportunities' : '/minhas-oportunidades',
'create' => $legacy ? '/management/opportunities/create' : ($navigation['create'] ?? null), 'profileWorkflow' => ! $legacy];
}
if ($upload) $result['documents'] = ['index' => '/management/portal-services', 'edit' => $editor];
return $result;
}
public function view(User $actor): array
{
$access = $this->access($actor);
abort_unless($access !== [], 403);
$counts = [];
if (isset($access['opportunities'])) {
$counts['opportunities'] = app(PortalProfileAccess::class)->scope(Opportunity::query(), $actor)
->selectRaw("COALESCE(workflow_context->>'working_state',status) as state, count(*) as total")
->groupByRaw("COALESCE(workflow_context->>'working_state',status)")->pluck('total', 'state');
}
if (isset($access['documents'])) {
$documents = app(DocumentManagementAccess::class);
$query = $documents->full($actor) ? DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')->whereNull('d.deleted_at')
: ($access['documents']['edit'] ? $documents->editorialResources($actor) : null);
$counts['documents'] = $query ? $query->select('r.published', DB::raw('count(*) as total'))->groupBy('r.published')->get()
->mapWithKeys(fn ($row) => [$row->published ? 'published' : 'unpublished' => (int) $row->total]) : [];
}
return ['access' => $access, 'counts' => $counts];
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
namespace App\Services;
use DOMDocument;
use DOMElement;
/** Supplement the six named agribusiness sections without changing corridor masters or source documents. */
final class CorridorAgribusiness
{
public function enrich(string $html, array $links = []): string
{
$data = json_decode(file_get_contents(__DIR__.'/../../database/data/corridor-investment-opportunities.json'), true, 512, JSON_THROW_ON_ERROR);
$doc = new DOMDocument;
$previous = libxml_use_internal_errors(true);
try {
$doc->loadHTML('<?xml encoding="UTF-8"><html><body>'.$html.'</body></html>', LIBXML_NONET);
$body = $doc->getElementsByTagName('body')->item(0);
if (! $body) {
return $html;
}
$changed = false;
foreach ($data['groups'] as $index => $group) {
$marker = 'agronegocio-oportunidades-'.($index + 1);
if ($doc->getElementById($marker)) {
continue;
}
foreach (iterator_to_array($body->childNodes) as $heading) {
if (! $heading instanceof DOMElement) {
continue;
}
$name = preg_replace('/^[a-f]\)\s*/u', '', trim($heading->textContent));
if ($name !== $group['name']) {
continue;
}
$chains = $heading->nextSibling;
while ($chains && ! $chains instanceof DOMElement) {
$chains = $chains->nextSibling;
}
// Insert only into the exact named section and its existing priority-chain paragraph.
if (! $chains || ! str_starts_with(trim($chains->textContent), 'Cadeias de valor prioritárias:')) {
continue;
}
$fragment = $doc->createDocumentFragment();
$label = $doc->createElement('p');
$strong = $doc->createElement('strong');
$strong->appendChild($doc->createTextNode('Oportunidades de investimento em agronegócio:'));
$label->appendChild($strong);
$fragment->appendChild($label);
$list = $doc->createElement('ul');
$list->setAttribute('id', $marker);
foreach ($group['opportunities'] as $itemIndex => $opportunity) {
$item = $doc->createElement('li');
$chain = $doc->createElement('strong');
$chain->appendChild($doc->createTextNode($opportunity['chain'].': '));
$item->appendChild($chain);
$item->appendChild($doc->createTextNode($opportunity['description']));
if (isset($links[$index][$itemIndex])) {
$item->appendChild($doc->createTextNode(' '));
$link = $doc->createElement('a');
$link->setAttribute('href', $links[$index][$itemIndex]);
$link->appendChild($doc->createTextNode('Conhecer a oportunidade e iniciar o roteiro'));
$item->appendChild($link);
}
$list->appendChild($item);
}
$fragment->appendChild($list);
$source = $doc->createElement('p');
$source->appendChild($doc->createTextNode('Fonte das oportunidades: '.$data['source']));
$fragment->appendChild($source);
$body->insertBefore($fragment, $chains->nextSibling);
$changed = true;
break;
}
}
return $changed ? implode('', array_map(fn ($node) => $doc->saveHTML($node), iterator_to_array($body->childNodes))) : $html;
} finally {
libxml_clear_errors();
libxml_use_internal_errors($previous);
}
}
}
@@ -0,0 +1,97 @@
<?php
namespace App\Services;
use App\Models\Opportunity;
use App\Models\Sector;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Ramsey\Uuid\Uuid;
/** Source-defined opportunities; never infer a master corridor, site or promoter. */
final class CorridorAgribusinessOpportunities
{
public const SOURCE_HASH = '1473b06c48aa234e771660e3f2a51b9d2e4e1943bd52d878e0ae854ee6657055';
/** Documentary cross-reference only; keep the two source designations and master records intact. */
public function portfolioOpportunityIds(\App\Models\InvestmentDossier $dossier): array
{
if (!$dossier->corridor_id || data_get($dossier->content, 'code') !== 'COR-02') return [];
// COR-02 is Beira e Centro in investment-portfolio.json. The seven entries are explicitly
// grouped under Corredor da Beira in the accepted source, not selected by province/name search.
$keys = collect($this->entries())->where('corridor', 'Corredor da Beira')->pluck('key');
return Opportunity::published()->whereIn('management_creation_key', $keys)
->where('workflow_context->corridor_agribusiness->source_sha256', self::SOURCE_HASH)->pluck('id')->all();
}
public const LIMITATIONS = 'Promotor, implantação exacta, área, montante, financiamento, viabilidade e licenças não indicados nesta fonte. O corredor é um enquadramento documental; não identifica uma parcela nem confirma disponibilidade.';
public function entries(): array
{
$data = json_decode(file_get_contents(database_path('data/corridor-investment-opportunities.json')), true, 512, JSON_THROW_ON_ERROR);
if ($data['source_sha256'] !== self::SOURCE_HASH) throw new \RuntimeException('Fonte divergente; reconciliar antes de cadastrar.');
$sector = Sector::where('slug', 'agronegocio')->where('is_active', true)->whereNull('deleted_at')->firstOrFail();
$entries = [];
foreach ($data['groups'] as $g => $group) {
foreach ($group['opportunities'] as $i => $item) {
$key = (string) Uuid::uuid5(Uuid::NAMESPACE_URL, 'investmoz:agronegocio:'.self::SOURCE_HASH.':'.$g.':'.$i);
$title = Str::limit($item['chain'].' — '.$group['name'], 150, '…');
$description = $item['description']."\n\nEnquadramento documental: ".$group['name'].".\n\nFonte: ".$data['source'].'; SHA-256 '.self::SOURCE_HASH.".\n\n".self::LIMITATIONS;
$entries[] = ['key' => $key, 'group' => $g, 'index' => $i, 'corridor' => $group['name'], 'chain' => $item['chain'],
'source' => $data['source'], 'source_sha256' => self::SOURCE_HASH, 'title' => $title,
'summary' => $item['description'], 'description' => $description, 'sector_id' => $sector->id];
}
}
if (count($entries) !== 34) throw new \RuntimeException('O lote deve conter as 34 entradas da fonte.');
return $entries;
}
public function register(User $actor, string $organization): array
{
// The caller uses the ordinary profile creation route and its approved scope.
abort_unless(str_starts_with(request()->path(), 'minhas-oportunidades'), 422);
return DB::transaction(function () use ($actor, $organization) {
DB::select('SELECT pg_advisory_xact_lock(231026, 34)');
$result = [];
foreach ($this->entries() as $entry) {
$record = Opportunity::withTrashed()->where('management_creation_key', $entry['key'])->first();
if ($record) {
abort_unless(app(PortalProfileAccess::class)->canView($actor, $record), 403);
abort_unless(data_get($record->workflow_context, 'corridor_agribusiness.source_sha256') === self::SOURCE_HASH, 409);
// Recovery never overwrites editorial changes, withdrawal or archive.
$result[] = ['id' => $record->id, 'created' => false, 'key' => $entry['key']];
continue;
}
$record = app(OpportunityManagement::class)->save($actor, ['creation_key' => $entry['key'], 'organization_id' => $organization,
'opportunity_category' => 'investment_project', 'sector_id' => $entry['sector_id'],
'title' => $entry['title'], 'summary' => $entry['summary'], 'description' => $entry['description'],
'canonical' => ['opportunity_category' => 'investment_project', 'official_title' => $entry['title'],
'executive_summary' => $entry['summary'], 'demand_statement' => $entry['description'], 'sector_id' => $entry['sector_id']],
'reason' => 'Cadastro privado das 34 oportunidades de agronegócio solicitado pela Tempo em 23/10/2026; não constitui publicação nem aprovação de execução.']);
$context = $record->workflow_context ?? [];
$context['corridor_agribusiness'] = $entry;
$context['promotional_summary'] = ['input' => ['title' => $entry['title'], 'summary' => $entry['summary'],
'description' => $entry['description'], 'source_reference' => $entry['source'].'; SHA-256 '.self::SOURCE_HASH,
'limitations' => self::LIMITATIONS, 'edition' => '', 'source_date' => '', 'territory_ids' => [], 'sector_id' => $entry['sector_id']]];
$record->workflow_context = $context;
$record->maturity_label = 'Em preparação';
$record->save();
DB::table('audit_events')->insert(['source_system' => 'corridor-agribusiness-registration', 'entity_type' => 'opportunity',
'action' => 'private_source_registered', 'actor_user_id' => $actor->id, 'data_classification' => 'restricted',
'after_data' => json_encode(['record_id' => $record->id, 'source' => $entry, 'no_publication' => true, 'no_master_relation_inferred' => true], JSON_THROW_ON_ERROR)]);
$result[] = ['id' => $record->id, 'created' => true, 'key' => $entry['key']];
}
return $result;
});
}
public function articleLinks(): array
{
$records = Opportunity::published()->whereIn('management_creation_key', array_column($this->entries(), 'key'))->get()->keyBy('management_creation_key');
$links = [];
foreach ($this->entries() as $entry) {
if ($record = $records->get($entry['key'])) $links[$entry['group']][$entry['index']] = '/oportunidades/'.$record->slug;
}
return $links;
}
}
+8 -2
View File
@@ -26,8 +26,9 @@ class DocumentManagementAccess
/** Restore the approved editorial operation without a global management grant. */
public function editorial(User $actor): bool
{
return config('portal.homologation_only', false)
&& in_array(strtolower(trim($actor->email)), config('homologation_editorial.accounts', []), true)
// Tempo's named editorial designation does not depend on whether the
// site's technical reviewer reservation is enabled.
return in_array(strtolower(trim($actor->email)), config('homologation_editorial.accounts', []), true)
&& app(PortalProfileAccess::class)->superuser($actor)
&& ! app(PortalProfileAccess::class)->has($actor, 'auditor')
&& app(PortalProfileAccess::class)->mfa($actor);
@@ -49,6 +50,11 @@ class DocumentManagementAccess
->where('d.external_id', $item['external_id']);
});
}
foreach (json_decode(file_get_contents(config_path('legacy_editorial_resources.json')), true, 512, JSON_THROW_ON_ERROR) as $item) {
// These exact original bytes contain the legacy identity/source.
// Destination upload IDs differ; the approved file scope does not.
$query->orWhere('d.sha256', $item['sha256']);
}
});
}
+130
View File
@@ -0,0 +1,130 @@
<?php
namespace App\Services;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Psr7\UriResolver;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpKernel\Exception\HttpException;
/** Reads verified originals without writing a derived edition or changing publication. */
final class EditorialReading
{
public function publicResources()
{
return DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')
->where('r.published', true)->where('r.rights_status', 'authorized')->where('d.rights_status', 'authorized')
->where('d.status', 'active')->where('d.data_classification', 'public')->whereNull('d.deleted_at')
->where('d.malware_scan_status', 'clean')->whereColumn('d.sha256', 'd.malware_scanned_sha256');
}
public function original(object $resource): ?array
{
$document = DB::table('documents')->find($resource->document_id);
if (! $document || $document->status !== 'active' || $document->malware_scan_status !== 'clean' || ! in_array($document->media_type, ['text/plain', 'application/json'], true)) {
return null;
}
$binary = app(VerifiedDocumentBinary::class)->respond($document);
$bytes = $binary instanceof BinaryFileResponse ? file_get_contents($binary->getFile()->getPathname()) : $binary->getContent();
if (! is_string($bytes) || strlen($bytes) > 10485760) {
return null;
}
$original = json_decode($bytes, true);
return is_array($original) && ($original['format'] ?? '') === 'investmoz-legacy-article-v1' && is_string($original['content_html'] ?? null) ? $original : null;
}
public function plain(string $html): string
{
return trim(html_entity_decode(strip_tags(preg_replace('~</(?:p|div|li|h[1-6]|tr)>|<br\s*/?>~i', "\n\n", $html)), ENT_QUOTES | ENT_HTML5, 'UTF-8'));
}
public function content(object $resource, ?array $original): array
{
return $resource->editorial_content ? json_decode($resource->editorial_content, true, 512, JSON_THROW_ON_ERROR)
: ['body' => $this->plain($original['content_html'] ?? ''), 'image_resource_id' => null, 'image_mode' => 'original', 'preserve_original_html' => true];
}
private function sourceUrl(mixed $url, ?string $base = null): ?string
{
if (! is_string($url) || trim($url) === '') {
return null;
}
try {
$absolute = $base ? (string) UriResolver::resolve(new Uri($base), new Uri($url)) : $url;
return filter_var($absolute, FILTER_VALIDATE_URL) && in_array(strtolower((string) parse_url($absolute, PHP_URL_SCHEME)), ['http', 'https'], true) ? $absolute : null;
} catch (\InvalidArgumentException) {
return null;
}
}
public function reading(object $resource, ?array $override = null, bool $private = false): array
{
$original = $this->original($resource);
$content = $override ?? $this->content($resource, $original);
abort_unless(trim($content['body'] ?? '') !== '', 404, 'Este documento não contém um artigo legível.');
$document = DB::table('documents')->find($resource->document_id);
// The original is verified above; ordinary text adaptations retain the binary gate too.
app(VerifiedDocumentBinary::class)->respond($document);
$mode = $content['image_mode'] ?? (! empty($content['image_resource_id']) ? 'selected' : 'none');
$originalImageUrl = $this->sourceUrl($original['image_source_url'] ?? null, $original['source_url'] ?? null);
$expectedImageSha = $original['image_sha256'] ?? null;
if (! $expectedImageSha && $originalImageUrl === 'https://investemoz.tempo.co.mz/images/tempo-background.png') {
foreach (json_decode(file_get_contents(config_path('legacy_editorial_resources.json')), true, 512, JSON_THROW_ON_ERROR) as $accepted) {
if ($accepted['sha256'] === $document->sha256) {
$expectedImageSha = $accepted['original_image_sha256'] ?? null;
break;
}
}
}
$images = $this->publicResources()->whereIn('d.media_type', ['image/png', 'image/jpeg', 'image/webp']);
$image = null;
if ($mode === 'selected' && ! empty($content['image_resource_id'])) {
$image = (clone $images)->where('r.id', $content['image_resource_id'])->first(['r.id', 'r.title']);
}
if ($mode === 'original' && $original) {
$image = (clone $images)->where(function ($q) use ($expectedImageSha, $originalImageUrl) {
$q->whereRaw('1=0');
if ($expectedImageSha) {
$q->orWhere('d.sha256', $expectedImageSha);
} elseif ($originalImageUrl) {
$q->orWhere('r.source_url', $originalImageUrl)->orWhere('d.original_source', $originalImageUrl);
}
})->first(['r.id', 'r.title']);
}
if ($image) {
// A public flag alone never licenses missing or changed image bytes.
$binary = (clone $images)->where('r.id', $image->id)->first(['d.*']);
try {
app(VerifiedDocumentBinary::class)->respond($binary);
} catch (HttpException) {
$image = null;
}
}
$originalFormat = $original && ($content['preserve_original_html'] ?? false) && $content['body'] === $this->plain($original['content_html']);
$html = $originalFormat ? app(LegacyContent::class)->sanitize($original['content_html'], true) : null;
$imageReferences = [];
if ($original) {
preg_match_all('~<img\b[^>]*\bsrc\s*=\s*([\'"])(.*?)\1~is', $original['content_html'], $matches);
foreach ($matches[2] as $source) {
$url = $this->sourceUrl(html_entity_decode($source, ENT_QUOTES | ENT_HTML5, 'UTF-8'), $original['source_url'] ?? null);
if ($url) {
$imageReferences[] = $url;
}
}
}
return ['id' => $resource->id, 'title' => $resource->title, 'description' => $resource->description,
'author' => $document->issuer, 'body' => $content['body'], 'html' => $html,
'image_url' => $image ? '/documentos/'.$image->id.'/view' : null,
'image_notice' => $mode === 'selected' && ! $image ? 'A imagem escolhida está indisponível para divulgação; a selecção mantém-se na edição.' : ($mode === 'original' && ! $image && ! empty($original['image_source_url']) ? 'Imagem de origem referenciada, mas sem exemplar público verificado na biblioteca. A referência original mantém-se preservada.' : null),
'source_url' => $this->sourceUrl($original['source_url'] ?? $resource->source_url),
'published_at' => $original['published_at'] ?? null, 'modified_at' => $original['modified_at'] ?? null,
'legacy_id' => $original['legacy_id'] ?? null, 'image_source_url' => $originalImageUrl, 'image_expected_sha256' => $expectedImageSha, 'image_references' => array_values(array_unique($imageReferences)),
'metadata_version' => $resource->metadata_version, 'private_preview' => $private,
'original_url' => $private ? '/management/documents/'.$resource->document_id.'/download' : '/documentos/'.$resource->id,
];
}
}
+36 -20
View File
@@ -26,7 +26,7 @@ class InvestmentCatalogue
'cluster_id' => ['nullable', 'uuid'], 'corridor_id' => ['nullable', 'uuid'], 'project_id' => ['nullable', 'uuid'],
'geo_location_id' => ['nullable', 'uuid'], 'education' => ['nullable', 'boolean'],
'zee_id' => ['nullable', 'uuid'], 'zfi_id' => ['nullable', 'uuid'], 'park_id' => ['nullable', 'uuid'],
'dimension' => ['nullable', 'in:cluster,corridor,zee,zfi'], 'view' => ['nullable', 'in:grid,list,map,combined'],
'dimension' => ['nullable', 'in:cluster,corridor,zee,zfi'], 'view' => ['nullable', 'in:grid,list,map,combined,corridors'],
'maturity_level' => ['nullable', 'integer', 'between:1,8'], 'facets' => ['nullable', 'array', 'max:20'], 'facets.*' => ['integer', 'min:1'],
'min_amount' => ['nullable', 'numeric', 'min:0', 'max:999999999999'],
'max_amount' => ['nullable', 'numeric', 'min:0', 'max:999999999999', ...($request->filled('min_amount') ? ['gte:min_amount'] : [])],
@@ -231,16 +231,25 @@ class InvestmentCatalogue
} elseif (in_array($relation, ['clusters', 'corridors'], true)) {
$query->whereIn('id', $this->publicStructureIds($relation, $foreign));
} else {
$query->whereIn('id', InvestmentPortfolioController::visible()->whereNotNull($foreign)->pluck($foreign));
$query->whereIn('id', app(OpportunityClassification::class)->targets('project')->select('id'));
}
}
if (! $management && $path !== [] && $relation !== 'locations') {
$dossiers = InvestmentPortfolioController::visible();
app(InvestmentGeography::class)->constrainDossiersToTerritories($dossiers,
app(InvestmentGeography::class)->descendants($path[array_key_last($path)]->id));
$query->whereIn('id', $dossiers->whereNotNull($foreign)->select($foreign));
$territories = app(InvestmentGeography::class)->descendants($path[array_key_last($path)]->id);
$query->where(fn ($area) => $area->whereIn('primary_territory_id', $territories)
->orWhereIn('id', $dossiers->whereNotNull($foreign)->select($foreign)));
}
$result[$relation] = $query->orderBy('name')->get(['id', 'name']);
if (! $management && $relation !== 'locations') {
$dossiers = InvestmentPortfolioController::visible()->whereIn($foreign, $result[$relation]->pluck('id'))
->orderBy('id')->get(['id', $foreign])->unique($foreign)->keyBy($foreign);
$result[$relation]->each(function ($record) use ($dossiers): void {
$record->url = isset($dossiers[$record->id]) ? '/carteira/'.$dossiers[$record->id]->id : null;
});
}
}
$zones = app(InvestmentZones::class)->visible();
@@ -250,24 +259,15 @@ class InvestmentCatalogue
['province_id', 'district_id', 'admin_post_id', 'locality_id', 'province', 'district', 'geo_location_id']));
}
$result['zones'] = $zones->orderBy('name')->get(['id', 'name', 'category']);
if (! $management && ($path !== [] || ! empty($territoryFilters['sector']))) {
$scope = \Illuminate\Support\Arr::only($territoryFilters, ['sector','province_id','district_id','admin_post_id','locality_id','include_national']);
$available = ['chain' => [], 'activity' => []];
$this->query($scope)->reorder()->chunkById(200, function ($records) use (&$available) {
foreach ($records as $record) {
foreach (app(OpportunityClassification::class)->publicRows($record) as $row) {
if (isset($available[$row['target_type']])) $available[$row['target_type']][] = $row['target_id'];
}
foreach (['subsector_id','economic_activity_id'] as $key) {
$id = $record->workflow_context['publication']['taxonomy'][$key] ?? null;
if ($id) $available['activity'][] = $id;
}
}
}, 'opportunities.id', 'id');
foreach ($available as $kind => $ids) {
$result['classification']['targets'][$kind] = collect($result['classification']['targets'][$kind])->whereIn('id', $ids)->values();
}
if (! $management && $path !== []) {
// A chain's documented scope comes from its public cluster, never
// from the opportunities surviving the current search.
$ids = app(OpportunityClassification::class)->targets('chain')
->whereIn('cluster_id', $result['clusters']->pluck('id'))->pluck('id');
$result['classification']['targets']['chain'] = collect($result['classification']['targets']['chain'])->whereIn('id', $ids)->values();
}
// Activities are a public classification catalogue, not evidence of
// investment availability in a territory or sector.
return $result;
}
@@ -292,6 +292,10 @@ class InvestmentCatalogue
}
}
});
if (!empty($selected['corridor_id'])) {
$dossier = InvestmentPortfolioController::visible()->where('corridor_id', $selected['corridor_id'])->first();
if ($dossier) $ids['corridor_id'] = array_values(array_unique([...($ids['corridor_id'] ?? []), ...app(CorridorAgribusinessOpportunities::class)->portfolioOpportunityIds($dossier)]));
}
return $ids;
}
@@ -308,6 +312,18 @@ class InvestmentCatalogue
/** @return array<string,mixed> */
public function context(int $id, bool $management = false): array
{
$opportunity = Opportunity::findOrFail($id);
if (! $management && data_get($opportunity->workflow_context, 'publication.purpose') === OpportunityPromotion::PURPOSE) {
// Promotional disclosure includes only its explicit public coverage.
// Private profiles, source evidence and other dossier relations are not auto-published.
$public = ['profile' => null, 'facets' => collect(), 'zones' => collect(), 'classification' => [], 'source_evidence' => [],
'primary_territory_path' => app(InvestmentGeography::class)->path($opportunity->primary_territory_id, true),
'opportunity_locations' => app(InvestmentRelations::class)->opportunityLocations($id)];
foreach (self::RELATIONS as $relation => $_) $public[$relation] = collect();
$public['locations'] = DB::table('opportunity_locations as l')->join('geo_locations as g', 'g.id', '=', 'l.geo_location_id')
->where('l.opportunity_id', $id)->where('g.data_classification', 'public')->whereNull('g.deleted_at')->get(['g.id', 'g.name']);
return $public;
}
$result = ['profile' => DB::table('opportunity_profiles')->where('opportunity_id', $id)->first(),
'facets' => DB::table('opportunity_facets as f')->join('investment_reference_options as o', 'o.id', '=', 'f.option_id')
->where('f.opportunity_id', $id)->get(['o.id', 'o.group', 'o.label'])];
+157
View File
@@ -0,0 +1,157 @@
<?php
namespace App\Services;
use App\Http\Controllers\InvestmentPortfolioController;
use App\Models\Opportunity;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
/** Resolve documented complements without creating opportunities or master relations. */
final class InvestmentComplements
{
private const TERMS = [
'irrigation' => ['Regadio', 'regadio|irrigação|irrigacao'],
'storage' => ['Silos e armazenagem', 'silos?|armazenagem|armazenamento'],
'cold' => ['Cadeia de frio', 'frio|refrigeração'],
'processing' => ['Processamento', 'processamento|transformação|moagem'],
'spinning' => ['Fiação', 'fiação|fiacao'],
'logistics' => ['Logística', 'logística|logistica|transporte'],
'certification' => ['Certificação', 'certificação|certificacao'],
'training' => ['Formação', 'formação|formacao'],
'finance' => ['Financiamento', 'financiamento|serviços financeiros'],
];
public function options(string $origin, ?string $territory = null): array
{
[$originKind, $originId] = array_pad(explode(':', $origin, 2), 2, '');
abort_unless(($originKind === 'portfolio' && Str::isUuid($originId)) || ($originKind === 'opportunity' && ctype_digit($originId)), 422, 'Origem complementar inválida.');
$principal = null;
$relationEvidence = [];
if (str_starts_with($origin, 'portfolio:')) {
$record = InvestmentPortfolioController::visible()->addSelect('version_no')->findOrFail(substr($origin, 10));
$text = trim(($record->content['focus'] ?? '')."\n".($record->content['gaps'] ?? '')."\n".($record->content['description'] ?? ''));
$reference = ['key' => $origin, 'title' => $record->title, 'href' => '/carteira/'.$record->id, 'version' => $record->version_no];
$ids = app(CorridorAgribusinessOpportunities::class)->portfolioOpportunityIds($record);
foreach (['projects' => 'project_id', 'clusters' => 'cluster_id', 'corridors' => 'corridor_id'] as $relation => $foreign) {
if ($record->$foreign) $ids = [...$ids, ...DB::table('opportunity_'.$relation)->where($foreign, $record->$foreign)->pluck('opportunity_id')->all()];
}
$basis = $record->source;
foreach (array_unique($ids) as $id) $relationEvidence[$id] = ['type' => 'portfolio', 'origin' => $origin, 'source' => $basis];
$serviceOrigins = [['dossier', $record->id]];
foreach (['project_id' => 'project', 'cluster_id' => 'cluster'] as $foreign => $type) {
if ($record->$foreign) $serviceOrigins[] = [$type, $record->$foreign];
}
} else {
$reference = app(InvestorContext::class)->reference($origin);
abort_unless($reference['kind'] === 'opportunity', 422);
$principal = Opportunity::published()->findOrFail($reference['id']);
$text = $principal->summary;
$basis = 'Resumo cadastrado da oportunidade '.$principal->reference;
$projects = DB::table('opportunity_projects')->where('opportunity_id', $principal->id)->pluck('project_id');
$links = DB::table('complementary_project_links')->whereIn('anchor_project_id', $projects)
->whereNull('deleted_at')->where('data_classification', 'public')->get(['id', 'anchor_project_id', 'complementary_project_id', 'relation_type', 'version_no']);
$targetProjects = DB::table('opportunity_projects')->whereIn('project_id', $links->pluck('complementary_project_id'))->get();
$ids = [$principal->id, ...$targetProjects->pluck('opportunity_id')->all()];
$relationEvidence[$principal->id] = ['type' => 'same_fiche', 'source' => $basis];
foreach ($targetProjects as $targetProject) $relationEvidence[$targetProject->opportunity_id] = ['type' => 'complementary_project',
'links' => $links->where('complementary_project_id', $targetProject->project_id)->values()->map(fn($link) => (array)$link)->all()];
foreach (Opportunity::published()->whereIn('id', $ids)->where('id', '!=', $principal->id)->get() as $linked) $text .= "\n".$linked->summary;
if (count($ids) > 1) $basis .= '; relações complementares públicas entre projectos';
$serviceOrigins = [['investment', (string) $principal->id]];
$classification = app(OpportunityClassification::class);
$principalChains = collect($classification->publicRows($principal))->where('target_type', 'chain')->filter(fn($r) => !empty($r['source_reference']));
$chains = $principalChains->pluck('target_id')->all();
if ($chains) {
Opportunity::published()->whereNot('id', $principal->id)->chunkById(100, function ($records) use (&$ids, &$relationEvidence, $chains, $principalChains, $classification) {
foreach ($records as $candidate) {
$related = collect($classification->publicRows($candidate))->where('target_type', 'chain')->filter(fn($r) => !empty($r['source_reference']));
$shared = array_intersect($chains, $related->pluck('target_id')->all());
if ($shared) {
$ids[] = $candidate->id;
$relationEvidence[$candidate->id] ??= ['type' => 'value_chain', 'principal' => $principalChains->whereIn('target_id', $shared)->values()->all(), 'target' => $related->whereIn('target_id', $shared)->values()->all()];
}
}
});
}
}
$territoryReference = $territory ? app(InvestorContext::class)->reference('territory:'.$territory) : null;
$candidates = Opportunity::published()->whereIn('id', array_unique($ids))->get();
$items = [];
foreach (self::TERMS as $code => [$label, $pattern]) {
if (!preg_match('/\b(?:'.$pattern.')\b/iu', $text, $match)) continue;
$offers = [];
foreach ($candidates as $candidate) {
// A common corridor alone does not prove the component. Keep the supporting excerpt.
if (!preg_match('/\b(?:'.$pattern.')\b/iu', $candidate->title."\n".$candidate->summary, $evidence)) continue;
// Mentioning a need for funding/training in a project's summary is not an offer.
if (in_array($code, ['certification', 'training', 'finance'], true) && !preg_match('/\b(?:'.$pattern.')\b/iu', $candidate->title)) continue;
if (in_array($code, ['certification', 'training', 'finance'], true) && preg_match('/\b(?:necessidade|pedido|procura)\b/iu', $candidate->title)) continue;
if ($territory && $candidate->primary_territory_id && !$this->compatible($territory, $candidate->primary_territory_id)) continue;
$offers[] = ['key' => 'opportunity:'.$candidate->id, 'title' => $candidate->title, 'href' => '/oportunidades/'.$candidate->slug,
'relationship' => $relationEvidence[$candidate->id] ?? null,
'version' => $candidate->management_version, 'evidence' => $evidence[0], 'location' => ($principal && $principal->id === $candidate->id ? 'Componente referido na própria ficha. ' : '').($candidate->primary_territory_id ? app(InvestorContext::class)->reference('territory:'.$candidate->primary_territory_id)['title'] : 'Implantação por definir')];
}
// Services are concrete published offerings explicitly associated with this source.
$services = [];
if (in_array($code, ['certification', 'training', 'finance'], true) && Schema::hasTable('partnership_origins') && Schema::hasTable('partnership_interests')) {
if ($serviceOrigins) {
$services = DB::table('portal_partnerships as p')->join('partnership_origins as o', 'o.partnership_id', '=', 'p.id')
->where(function ($q) use ($serviceOrigins) {
foreach ($serviceOrigins as [$type, $id]) $q->orWhere(fn($q) => $q->where('o.type', $type)->where('o.target_id', $id));
})->where('p.status', 'published')->where('p.kind', 'offering')
->where('p.partnership_type', $code === 'finance' ? 'finance' : 'training')
->when($code !== 'finance', fn($q) => $q->where(fn($q) => $q->where('p.title', 'ilike', '%'.$match[0].'%')->orWhereRaw("p.payload->>'summary' ILIKE ?", ['%'.$match[0].'%'])))->distinct()->get(['p.id', 'p.title'])
->filter(function ($p) use ($territory) {
if (!$territory) return true;
$locations = DB::table('partnership_locations')->where('partnership_id', $p->id)->pluck('geo_location_id');
return $locations->isEmpty() || $locations->contains(fn($id) => $this->compatible($territory, $id));
})->map(fn ($p) => ['key' => 'partnership:'.$p->id, 'title' => $p->title, 'href' => '/parcerias/'.$p->id, 'location' => 'Consultar âmbito na oferta'])->values()->all();
}
}
$excerpt = collect(explode("\n", $text))->first(fn($line) => preg_match('/\b(?:'.$pattern.')\b/iu', $line));
$items[] = ['code' => $code, 'title' => $label, 'origin' => $reference, 'territory_id' => $territory, 'territory' => $territoryReference,
'source' => $basis, 'excerpt' => $excerpt, 'source_sha256' => hash('sha256', $text), 'opportunities' => $offers, 'services' => $services];
}
return $items;
}
private function compatible(string $a, string $b): bool
{
if ($a === $b) return true;
$geo = app(InvestmentGeography::class);
return in_array($b, $geo->descendants($a), true) || in_array($a, $geo->descendants($b), true);
}
public function resolve(array $input, array $previous = []): array
{
$resolved = [];
foreach ($input as $choice) {
$target = $choice['target'] ?? null;
$old = collect($previous)->first(fn($r) => $r['origin']['key'] === $choice['origin'] && $r['code'] === $choice['code'] && ($r['target']['key'] ?? null) === $target && ($r['territory_id'] ?? null) === ($choice['territory_id'] ?? null));
try {
$item = collect($this->options($choice['origin'], $choice['territory_id'] ?? null))->firstWhere('code', $choice['code']);
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
if (!$old) throw $e;
$item = null;
} catch (\Symfony\Component\HttpKernel\Exception\HttpException $e) {
if (!$old || $e->getStatusCode() !== 404) throw $e;
$item = null;
}
$offer = $target && $item ? collect([...$item['opportunities'], ...$item['services']])->firstWhere('key', $target) : null;
if ($old && (!$item || ($target && !$offer) || (!$target && ($item['opportunities'] || $item['services'])))) {
$resolved[$old['key']] = [...$old, 'availability' => 'review_required'];
continue;
}
abort_unless($item, 422, 'Complemento sem referência documentada neste investimento.');
abort_if($target && !$offer, 422, 'A oferta complementar deixou de estar disponível neste contexto.');
abort_if(!$target && ($item['opportunities'] || $item['services']), 422, 'Já existe uma oferta relacionada; seleccione a ficha correspondente.');
$key = hash('sha256', $choice['origin'].'|'.$choice['code'].'|'.($target ?? 'idea').'|'.($choice['territory_id'] ?? ''));
$resolved[$key] = ['key' => $key, 'origin' => $item['origin'], 'code' => $item['code'], 'title' => $item['title'],
'kind' => $target ? (str_starts_with($target, 'opportunity:') ? 'opportunity' : 'service') : 'need',
'target' => $offer, 'territory_id' => $item['territory_id'], 'territory' => $item['territory'], 'source' => $item['source'], 'excerpt' => $item['excerpt'], 'source_sha256' => $item['source_sha256']];
}
return array_values($resolved);
}
}
+85
View File
@@ -0,0 +1,85 @@
<?php
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Arr;
/** An identified opportunity within the authorised dossier, never the account's last choice. */
final class InvestmentConsumerScope
{
public function key(array $context, ?string $key, bool $required = false): ?string
{
$choices = $context['opportunity_choices'] ?? [];
$key = $key ?: (count($choices) === 1 ? $choices[0]['opportunity']['key'] : null);
abort_if($key && !collect($choices)->contains('opportunity.key', $key), 422, 'Escolha uma oportunidade deste dossier.');
abort_if($required && count($choices) > 1 && !$key, 422, 'Escolha a oportunidade em tratamento.');
return $key;
}
public function facts(array $payload, array $context, ?string $key): array
{
$key = $this->key($context, $key);
$path = $payload['investor_path'] ?? [];
$defaults = ['land_needed' => 'unknown', 'external_funding' => 'unknown', 'viability' => 'unknown'];
// A dossier-wide dispensation cannot decide a distinct opportunity's needs.
if ($key && count($context['opportunity_choices'] ?? []) > 1) return [...$defaults, ...($path['opportunity_facts'][$key] ?? [])];
if ($key && isset($path['opportunity_facts'][$key])) return [...$defaults, ...$path['opportunity_facts'][$key]];
if (count($context['opportunity_choices'] ?? []) > 1) return $defaults;
return [...$defaults, ...($path['facts'] ?? [])];
}
public function context(array $context, ?string $key): array
{
$choices = $context['opportunity_choices'] ?? [];
if (!$choices) return $context;
$key = $key ?: (count($choices) === 1 ? $choices[0]['opportunity']['key'] : null);
$choice = $key ? collect($choices)->firstWhere('opportunity.key', $key) : null;
abort_if($key && !$choice, 422, 'Escolha uma oportunidade deste dossier.');
$specific = ['project_summary', 'sector_id', 'sector', 'province_id', 'district_id', 'admin_post_id', 'locality_id', 'province', 'district', 'location', 'special_zone_id', 'investment_regime'];
$context['fields'] = Arr::except($context['fields'], $specific);
$context['provenance'] = Arr::except($context['provenance'] ?? [], $specific);
$context['selection'] = Arr::except($context['selection'], ['opportunity', 'territory', 'park']);
if (!$choice) {
$context['opportunity_choices'] = [];
$context['requirements_assessment']['requirements'] = [];
return $context;
}
$context['selection']['opportunity'] = $choice['opportunity'];
$context['fields'] = [...$context['fields'], ...Arr::only($choice['opportunity']['fields'], $specific)];
foreach (Arr::only($choice['opportunity']['fields'], $specific) as $field => $value) $context['provenance'][$field] = Arr::only($choice['opportunity'], ['key','kind','id','title','version','sha256']);
unset($context['selection']['sector']);
if (!empty($context['fields']['sector_id'])) $context['selection']['sector'] = app(InvestorContext::class)->reference('sector:'.$context['fields']['sector_id']);
if ($choice['territory']) {
$context['selection']['territory'] = $choice['territory'];
$context['fields'] = [...Arr::except($context['fields'], array_keys(InvestmentGeography::FIELDS)), ...$choice['territory']['fields']];
foreach ($choice['territory']['fields'] as $field => $value) $context['provenance'][$field] = Arr::only($choice['territory'], ['key','kind','id','title','version','sha256']);
} else {
$context['fields'] = Arr::except($context['fields'], ['province_id', 'district_id', 'admin_post_id', 'locality_id', 'province', 'district', 'location']);
}
$park = $choice['park'] ?? null;
if ($park) $context['selection']['park'] = $park;
$context['fields']['special_zone_id'] = $park['id'] ?? '';
$context['fields']['investment_regime'] = $park['fields']['investment_regime'] ?? 'general';
$context['opportunity_choices'] = [$choice];
$context['requirements_assessment']['requirements'] = array_values(array_filter($context['requirements_assessment']['requirements'] ?? [], fn($r) => $r['opportunity_key'] === $key));
$context['consumer_opportunity'] = $choice;
$context['complements'] = array_values(array_filter($context['complements'] ?? [], fn($c) => ($c['origin']['key'] ?? null) === $key || ($c['target']['key'] ?? null) === $key || in_array(substr($c['origin']['key'] ?? '',10),array_column($choice['portfolio_origins'] ?? [],'id'),true)));
$context['dependency_hash'] = app(InvestorContext::class)->dependencyHash(['opportunity_choices'=>[$choice], 'fields'=>Arr::except($context['fields'],['project_title','project_summary']), 'complements'=>$context['complements']]);
return $context;
}
public function bind(User $actor, string $id, array $payload, bool $required = false): array
{
$case = app(VirtualDesk::class)->case($actor, $id, true);
$context = app(InvestorContext::class)->state($case);
$scoped = $this->context($context, $payload['context_opportunity'] ?? null);
$choice = $scoped['consumer_opportunity'] ?? null;
abort_if($required && count($context['opportunity_choices'] ?? []) > 1 && !$choice, 422, 'Escolha a oportunidade em tratamento antes de submeter.');
if (!$choice) return Arr::except($payload, ['context_opportunity', 'context_opportunity_snapshot']);
// Canonical server snapshot; client text cannot establish a different origin or territory.
$payload['context_opportunity'] = $choice['opportunity']['key'];
$payload['context_opportunity_snapshot'] = json_encode(['context_id' => $id, 'revision' => $context['revision'], 'choice' => $choice, 'requirements' => $scoped['requirements_assessment'] ?? null], JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
return $payload;
}
}
@@ -0,0 +1,48 @@
<?php
namespace App\Services;
/** Preparation within the existing path; no licence, DUAT or fiscal benefit is granted. */
final class InvestmentContextRequirements
{
public const VERSION = 'context-requirements-20261023';
public function assess(array $context): array
{
$rows = [];
foreach ($context['opportunity_choices'] ?? [] as $choice) {
$activities = $choice['activities'] ?? [];
$key = $choice['opportunity']['key'];
$common = ['opportunity_key' => $key, 'opportunity_title' => $choice['opportunity']['title']];
$add = function ($code, $title, $state, $step, $authority, $documents, $basis, $url) use (&$rows, $common) {
$rows[] = [...$common, 'code' => $code, 'title' => $title, 'state' => $state, 'step' => $step,
'authority' => $authority, 'documents' => $documents, 'basis' => $basis, 'source_url' => $url,
'decision' => 'Preparação; enquadramento e acto concreto conservam confirmação na etapa correspondente.'];
};
$add('geography', 'Localização e geolocalização', $choice['territory'] ? 'selected' : 'missing', 'S09', 'Cadastro territorial e serviços de cadastro competentes',
['Referência administrativa confirmada', 'Planta/esboço e coordenadas quando disponíveis; não inventar polígonos'],
'Referência territorial escolhida no dossier; não comprova disponibilidade nem direito de uso.', '/investimentos');
$add('land', 'Acesso à terra e DUAT', 'conditional', 'S10', 'Serviços de cadastro e órgão competente para o pedido territorial concreto',
['Fundamento do uso: DUAT, arrendamento ou situação declarada', 'Planta/esboço do local; documentação do pedido correspondente'],
'Regulamento de Investimento, artigos 9(1)(c)–(d) e 14(1)(c)–(d). A preparação distingue DUAT e arrendamento; escolher território não atribui terra.', PrivateInvestmentPreparation::SOURCE.'#page=42');
$add('environment', 'Enquadramento ambiental', 'conditional', 'S08', 'Autoridade de Avaliação do Impacto Ambiental; competência central/provincial conforme categorização',
['Descrição da actividade, dimensão e localização', 'Instrumento ambiental definido pela categorização competente; acto e condições quando emitidos'],
'Decreto 54/2015: a natureza, dimensão e localização determinam o enquadramento; a categoria não é deduzida do corredor ou do sector.', 'https://sibmoz.gov.mz/legal-framework/');
$add('sector', 'Licenciamento sectorial', $activities ? 'declared' : 'missing', 'S08', 'BAU, IP e autoridade sectorial competente conforme a actividade',
['Actividades efectivas e modalidade', 'Requerimento e anexos da modalidade aplicável; acto quando emitido'],
'Identificar as actividades efectivas antes de seleccionar o procedimento. O nome Agronegócio não escolhe uma licença única.', 'https://www.bau.gov.mz/nossos-servicos/');
foreach (['processing' => ['Licenciamento industrial', 'Decreto 22/2014'], 'trade' => ['Licenciamento comercial', 'Decreto 34/2013']] as $activity => [$title, $basis]) {
if (in_array($activity, $activities, true)) $add($activity, $title, 'conditional', 'S08', 'BAU, IP / entidade licenciadora da modalidade',
['Caracterização do estabelecimento e actividade', 'Documentos e regime aplicáveis, incluindo simplificado ou mera comunicação quando preenchidas as suas condições'],
$basis.'; regimes 39/2017 e 27/2023 no catálogo oficial BAU. Dimensão e modalidade ainda devem ser especificadas; não se selecciona automaticamente o regime.', 'https://www.bau.gov.mz/legislacao/');
}
foreach (['water' => 'Uso de água', 'forestry' => 'Actividade florestal', 'animal' => 'Actividade pecuária', 'transport' => 'Transporte e logística'] as $activity => $title) {
if (in_array($activity, $activities, true)) $add($activity, $title, 'undetermined', 'S08', 'Autoridade sectorial competente a identificar para a modalidade declarada',
['Modalidade e dimensão da actividade', 'Fonte e documentação sectorial específica a associar ao dossier'],
'Actividade declarada; a referência do lote não contém o procedimento específico. Não se considera dispensada ou autorizada.', 'https://www.apiex.gov.mz/wp-content/uploads/2024/01/Mozambique-Business-Licensing-Guide.pdf');
}
}
return ['version' => self::VERSION, 'requirements' => $rows,
'fiscal_notice' => 'Localização, corredor, cluster ou projecto âncora não activam benefícios fiscais. O simulador mantém as condições, fonte, período e âmbito próprios; condições ausentes permanecem indeterminadas.'];
}
}
@@ -0,0 +1,59 @@
<?php
namespace App\Services;
use App\Models\Opportunity;
use Illuminate\Support\Facades\DB;
final class InvestmentCorridorContext
{
public function key(Opportunity $record): ?string
{
$name = data_get($record->workflow_context, 'corridor_agribusiness.corridor');
foreach (config('investment_corridors') as $key => $entry) if ($entry['name'] === $name) return $key;
return null;
}
public function provinces(string $key): array
{
$entry = config('investment_corridors.'.$key);
abort_unless(is_array($entry), 422, 'Seleccione um corredor disponível.');
$records = app(InvestmentGeography::class)->publicReferences()->where('location_type', 'province')
->whereIn('source_code', $entry['provinces'])->get(['id', 'name', 'parent_id', 'location_type', 'source_code'])->all();
abort_unless(count($records) === count($entry['provinces']), 503, 'Não foi possível recuperar todas as províncias deste corredor. Tente novamente.');
return $records;
}
public function compatible(string $key, ?string $territory): bool
{
if (!$territory) return true;
$path = app(InvestmentGeography::class)->path($territory);
return $path !== [] && in_array($path[0]->id, array_column($this->provinces($key), 'id'), true);
}
/** Both membership in the selected area and documented public zone coverage are required. */
public function parks(?string $key, ?string $territory): array
{
if (!$key || !$territory) return [];
abort_unless($this->compatible($key, $territory), 422, 'A localização não pertence ao corredor escolhido.');
$selectedPath = array_column(app(InvestmentGeography::class)->path($territory), 'id');
$candidates = app(InvestmentZones::class)->visible()->whereIn('category', ['industrial_park', 'zee', 'zfi'])->orderBy('name')->get();
$result = [];
foreach ($candidates as $zone) {
$ids = DB::table('investment_zone_locations')->where('zone_id', $zone->id)->where('validation_status', 'validated')
->whereIn('scope_type', ['legal_scope', 'administrative', 'site'])
->where(fn($q)=>$q->whereNull('valid_from')->orWhereDate('valid_from','<=',today()))
->where(fn($q)=>$q->whereNull('valid_until')->orWhereDate('valid_until','>=',today()))->get(['geo_location_id','scope_type'])->all();
if ($zone->primary_territory_id) $ids[] = (object)['geo_location_id'=>$zone->primary_territory_id,'scope_type'=>'site'];
foreach ($ids as $link) {
$id=$link->geo_location_id;
if (!$this->compatible($key, $id)) continue;
$path = array_column(app(InvestmentGeography::class)->path($id), 'id');
if (in_array($territory, $path, true) || ($link->scope_type==='legal_scope' && in_array($id, $selectedPath, true))) {
$result[] = $zone->only(['id', 'name', 'category', 'version_no']); break;
}
}
}
return $result;
}
}
@@ -0,0 +1,93 @@
<?php
namespace App\Services;
use App\Http\Controllers\InvestmentPortfolioController;
use App\Models\Opportunity;
use Illuminate\Support\Facades\DB;
/** Multiple identified references in the existing encrypted investment context. */
final class InvestmentOpportunityChoices
{
public function portfolioOrigins(array $ids, int $opportunity): array
{
$origins = [];
foreach (array_unique($ids) as $id) {
$record = InvestmentPortfolioController::visible()->addSelect('version_no')->findOrFail($id);
$kind = null;
if (in_array($opportunity, app(CorridorAgribusinessOpportunities::class)->portfolioOpportunityIds($record), true)) $kind = 'corridor';
foreach (['projects' => 'project_id', 'clusters' => 'cluster_id', 'corridors' => 'corridor_id'] as $relation => $foreign) {
if ($record->$foreign && DB::table('opportunity_'.$relation)->where('opportunity_id', $opportunity)->where($foreign, $record->$foreign)->exists()) $kind = rtrim($relation, 's');
}
abort_unless($kind, 422, 'A associação escolhida não pertence a esta oportunidade.');
$origins[] = ['id' => $record->id, 'title' => $record->title, 'kind' => $kind, 'href' => '/carteira/'.$record->id,
'version' => $record->version_no, 'sha256' => hash('sha256', json_encode([$record->title, $record->source, $record->content], JSON_THROW_ON_ERROR))];
}
return $origins;
}
/** Add explicitly consulted associations without resetting saved geography or deliberate answers. */
public function mergeEntries(array $saved, array $entries): array
{
foreach ($entries as $entry) {
$index = array_search($entry['opportunity']['key'], array_column(array_column($saved, 'opportunity'), 'key'), true);
if ($index === false) { $saved[] = $entry; continue; }
$origins = collect([...($saved[$index]['portfolio_origins'] ?? []), ...($entry['portfolio_origins'] ?? [])])->unique('id')->values()->all();
if ($origins) $saved[$index]['portfolio_origins'] = $origins;
}
return $saved;
}
public function resolve(array $choices, array $previous = [], bool $pending = false): array
{
$result = [];
foreach ($choices as $choice) {
$ref = app(InvestorContext::class)->reference($choice['source']);
abort_unless($ref['kind'] === 'opportunity', 422, 'Seleccione uma ficha de oportunidade.');
$record = Opportunity::findOrFail($ref['id']);
$old = collect($previous)->first(fn ($row) => $row['opportunity']['key'] === $ref['key']);
$origins = $this->portfolioOrigins($choice['portfolio_origins'] ?? array_column($old['portfolio_origins'] ?? [], 'id'), (int) $record->id);
$corridors = app(InvestmentCorridorContext::class);
$corridor = $choice['corridor_key'] ?? $old['corridor_key'] ?? $corridors->key($record);
$provinces = $corridor ? $corridors->provinces($corridor) : [];
$documented = $record->primary_territory_id;
$territory = array_key_exists('territory', $choice) ? $choice['territory'] : ($documented ? 'territory:'.$documented : null);
if (!$territory && count($provinces) === 1) $territory = 'territory:'.$provinces[0]->id;
if ($corridor && $territory) abort_unless($corridors->compatible($corridor, substr($territory, 10)), 422, 'A localização não pertence ao corredor escolhido.');
$previousTerritory = $old['territory']['key'] ?? null;
$newAlternative = $documented && $territory !== 'territory:'.$documented
&& (! $old || ($old['documented_territory_id'] ?? null) !== $documented || $territory !== $previousTerritory);
$corridorChanged = $old && ($old['corridor_key'] ?? $corridors->key($record)) !== $corridor;
$incompatibleOld = $corridorChanged && $previousTerritory && !$corridors->compatible($corridor, substr($previousTerritory, 10));
if (!$pending && ($newAlternative || ($previousTerritory && $territory !== $previousTerritory && !$incompatibleOld))) {
abort_unless(! empty($choice['confirm_location_change']) && mb_strlen(trim($choice['location_reason'] ?? '')) >= 20,
422, 'Uma localização documentada ou já confirmada só muda por escolha explícita, com confirmação e motivo.');
}
$location = $territory ? app(InvestorContext::class)->reference($territory) : null;
abort_unless($location === null || $location['kind'] === 'territory', 422);
$parkKey = array_key_exists('park', $choice) ? $choice['park'] : ($old['park']['key'] ?? null);
$park = $parkKey ? app(InvestorContext::class)->reference($parkKey) : null;
if ($park) {
$eligible = $corridor ? array_column($corridors->parks($corridor, $location['id'] ?? null), 'id') : [];
if (!in_array($park['id'], $eligible, true)) {
abort_if(!empty($choice['park']), 422, 'A zona ou parque não é compatível com a localização desta oportunidade.');
$park = null;
}
abort_unless(!$park || $park['kind'] === 'park', 422);
}
$result[] = ['opportunity' => $ref, 'territory' => $location, ...($origins ? ['portfolio_origins' => $origins] : []),
'park' => $park,
'corridor_key' => $corridor, 'corridor_name' => $corridor ? config('investment_corridors.'.$corridor.'.name') : null,
'allowed_provinces' => array_map(fn($p)=>(array)$p, $provinces),
'territory_path' => $location ? array_map(fn ($node) => \Illuminate\Support\Arr::only((array) $node, ['id', 'name', 'parent_id', 'location_type']), app(InvestmentGeography::class)->path($location['id'], true)) : [],
'activities' => $choice['activities'] ?? ($old['activities'] ?? []),
'documented_territory_id' => $documented, 'location_reason' => $choice['location_reason'] ?? '',
'location_state' => $location ? ($documented && $territory === 'territory:'.$documented ? 'documented' : 'investor_selected') : 'undetermined',
'location_changed_explicitly' => $old && $territory === $previousTerritory ? (bool) ($old['location_changed_explicitly'] ?? false) : (bool) ($choice['confirm_location_change'] ?? false),
'coverage' => data_get($record->workflow_context, 'corridor_agribusiness.corridor'),
'source' => data_get($record->workflow_context, 'corridor_agribusiness.source'),
'source_sha256' => data_get($record->workflow_context, 'corridor_agribusiness.source_sha256')];
}
return $result;
}
}
+200 -14
View File
@@ -27,6 +27,67 @@ final class InvestorContext
public function __construct(private readonly VirtualDesk $desk) {}
public function draft(User $actor, string $id): ?array
{
$case = $this->desk->case($actor, $id, true);
$draft = $this->desk->decode($case->payload)['investor_path']['context_drafts'][(string)$actor->id] ?? null;
return $draft ? [...$draft, 'needs_review' => (int)$draft['version'] !== (int)$case->version] : null;
}
public function saveDraft(User $actor, string $id, array $input): array
{
$case=$this->desk->case($actor,$id,true);
abort_if($case->closed_at,409);
$sequence = (int)($input['draft_sequence'] ?? 0);
$entrySources=Validator::make($input,['entry_sources'=>'sometimes|array|max:200','entry_sources.*'=>'string|max:100'])->validate()['entry_sources']??[];
unset($input['draft_sequence'],$input['entry_sources']);
abort_unless($sequence > 0, 422);
$data = $this->input($input);
if (isset($data['complements'])) app(InvestmentComplements::class)->resolve($data['complements'], $this->state($case)['complements'] ?? []);
foreach ($data['selection'] as $slot=>$key) {
if (!$key) continue;
$ref=$this->reference($key);
abort_unless(in_array($ref['kind'],$slot==='opportunity'?['opportunity','business']:[$slot],true),422,'Referência incompatível com a escolha.');
}
foreach ($data['opportunity_choices'] ?? [] as $choice) {
$ref = $this->reference($choice['source']);
abort_unless($ref['kind'] === 'opportunity', 422);
$record = Opportunity::findOrFail($ref['id']);
app(InvestmentOpportunityChoices::class)->portfolioOrigins($choice['portfolio_origins'] ?? [], (int) $record->id);
$key = $choice['corridor_key'] ?? app(InvestmentCorridorContext::class)->key($record);
if (!empty($choice['territory'])) {
$location = $this->reference($choice['territory']);
abort_unless($location['kind'] === 'territory', 422);
if ($key) abort_unless(app(InvestmentCorridorContext::class)->compatible($key, $location['id']), 422, 'A localização não pertence ao corredor escolhido.');
}
}
if (isset($data['opportunity_choices'])) app(InvestmentOpportunityChoices::class)->resolve($data['opportunity_choices'], $this->state($case)['opportunity_choices'] ?? [], true);
if (!empty($data['selection']['park'])) {
$first=$data['opportunity_choices'][0] ?? null;
$source=$first['source'] ?? $data['selection']['opportunity'] ?? null;
if ($source && str_starts_with($source,'opportunity:')) {
$key=$first['corridor_key'] ?? app(InvestmentCorridorContext::class)->key(Opportunity::findOrFail(substr($source,12)));
if ($key) {
$territory=$first['territory'] ?? $data['selection']['territory'] ?? null;
$ids=app(InvestmentCorridorContext::class)->provinces($key);
$territory=$territory ? substr($territory,10) : (count($ids)===1?$ids[0]->id:null);
$park=$this->reference($data['selection']['park']);
abort_unless($park['kind']==='park' && in_array($park['id'],array_column(app(InvestmentCorridorContext::class)->parks($key,$territory),'id'),true),422,'A zona ou parque não tem enquadramento compatível documentado nesta localização.');
}
}
}
return DB::transaction(function () use ($actor, $id, $data, $sequence, $entrySources) {
$case = $this->desk->case($actor, $id, true, true);
abort_if($case->closed_at || (int)$case->version !== (int)$data['version'], 409, 'O dossier mudou. Recupere os dados antes de continuar.');
$payload = $this->desk->decode($case->payload);
$previous = $payload['investor_path']['context_drafts'][(string)$actor->id] ?? [];
abort_if(($previous['draft_sequence'] ?? 0) > $sequence, 409, 'Existe um rascunho mais recente. Recupere o dossier antes de continuar.');
$payload['investor_path']['context_drafts'][(string)$actor->id] = [...$data, 'draft_sequence' => $sequence, 'entry_sources'=>array_values(array_unique([...($previous['entry_sources']??[]),...$entrySources]))];
DB::table('desk_cases')->where('id', $id)->update(['payload' => $this->desk->encode($payload)]);
return ['saved' => true];
});
}
public function installed(): bool
{
return Schema::hasTable('investor_navigation_preferences');
@@ -51,6 +112,7 @@ final class InvestorContext
}
}
}
if (!empty($context['opportunity_choices']) && !array_key_exists('park', $context['opportunity_choices'][0])) $context['opportunity_choices'][0]['park'] = $context['selection']['park'] ?? null;
$zone = $context['selection']['park'] ?? null;
$context['fields']['investment_regime'] = $zone['fields']['investment_regime'] ?? 'general';
$context['fields']['special_zone_id'] = $zone['id'] ?? '';
@@ -60,6 +122,8 @@ final class InvestorContext
public function dependencyHash(array $selection): string
{
unset($selection['portfolio'], $selection['selection']['portfolio']);
if (isset($selection['opportunity_choices'])) $selection['opportunity_choices'] = array_map(fn($choice)=>Arr::except($choice,['portfolio_origins']),$selection['opportunity_choices']);
return hash('sha256', json_encode(Arr::sortRecursive($selection), JSON_THROW_ON_ERROR));
}
@@ -105,6 +169,18 @@ final class InvestorContext
if ($o->primary_territory_id) {
$fields = [...$fields, ...$this->territoryFields($o->primary_territory_id)];
}
} elseif ($kind === 'portfolio') {
abort_unless(Str::isUuid($id), 422);
$dossier = \App\Http\Controllers\InvestmentPortfolioController::visible()->findOrFail($id);
$title = $dossier->title;
$href = '/carteira/'.$id;
// Documentary coverage is not the investor's chosen implantation.
$version = DB::table('investment_dossiers')->where('id', $id)->value('version_no');
$fields = ['project_title' => $title, 'project_summary' => $dossier->content['description'] ?? $dossier->content['focus'] ?? ''];
$state = 'documented_origin';
$zoneAnalysis = ['source' => $dossier->source, 'documented_sector' => $dossier->content['sector'] ?? null,
'documented_location' => $dossier->content['location'] ?? $dossier->content['region'] ?? null,
'geography' => app(InvestmentRelations::class)->dossier($dossier), 'available_opportunity' => false];
} elseif ($kind === 'business') {
abort_unless(config('existing-business.enabled') && Str::isUuid($id), 404);
$o = ExistingBusiness::published()->with('sector')->findOrFail($id);
@@ -268,7 +344,7 @@ final class InvestorContext
private function input(array $input): array
{
abort_if(array_diff(array_keys($input), ['version', 'selection', 'declarations', 'replace_fields', 'preview_hash', 'operation_key', 'confirm', 'document_links', '_token']), 422, 'Campo de controlo desconhecido.');
abort_if(array_diff(array_keys($input), ['complements', 'opportunity_choices', 'version', 'selection', 'declarations', 'replace_fields', 'preview_hash', 'operation_key', 'confirm', 'document_links', '_token']), 422, 'Campo de controlo desconhecido.');
abort_if(! app(PrivateInvestmentPreparation::class)->available() && (! empty($input['document_links']) || array_intersect(array_keys(is_array($input['declarations'] ?? null) ? $input['declarations'] : []), PrivateInvestmentPreparation::FIELDS)), 409, 'Esta preparação ainda não está disponível neste ambiente. As respostas foram preservadas.');
@@ -280,7 +356,18 @@ final class InvestorContext
$inputRules['declarations.'.$question['key']] = isset($question['options']) ? ['nullable', Rule::in(array_keys($question['options']))] : ['nullable', 'regex:/^\d+(?:\.\d+)?$/D', 'numeric', 'min:0', 'max:999999999999999999'];
}
return Validator::make($input, [...$inputRules, 'version' => 'required|integer|min:1', 'selection' => 'present|array:opportunity,sector,territory,park',
return Validator::make($input, [...$inputRules,
'complements' => 'sometimes|array|max:100', 'complements.*' => 'array:origin,code,target,territory_id',
'complements.*.origin' => 'required|string|max:100', 'complements.*.code' => 'required|string|max:40',
'complements.*.target' => 'nullable|string|max:100', 'complements.*.territory_id' => 'nullable|uuid',
'opportunity_choices' => 'sometimes|array|max:100', 'opportunity_choices.*' => 'array:source,territory,location_reason,confirm_location_change,activities,corridor_key,portfolio_origins,park',
'opportunity_choices.*.portfolio_origins' => 'sometimes|array|max:100', 'opportunity_choices.*.portfolio_origins.*' => 'uuid',
'opportunity_choices.*.corridor_key' => ['sometimes', 'nullable', Rule::in(array_keys(config('investment_corridors')))],
'opportunity_choices.*.activities' => 'sometimes|array', 'opportunity_choices.*.activities.*' => 'string|distinct|in:production,processing,trade,transport,water,forestry,animal',
'opportunity_choices.*.park' => 'sometimes|nullable|string|max:100',
'opportunity_choices.*.source' => 'required|string|distinct|max:100', 'opportunity_choices.*.territory' => 'nullable|string|max:100',
'opportunity_choices.*.location_reason' => 'nullable|string|max:2000', 'opportunity_choices.*.confirm_location_change' => 'sometimes|boolean',
'version' => 'required|integer|min:1', 'selection' => 'present|array:portfolio,opportunity,sector,territory,park',
'selection.*' => 'nullable|string|max:100', 'declarations' => 'nullable|array:project_title,project_summary,currency,expected_start_date,investor_origin,person_type,entity_stage,tax_residency_country,fiscal_residency_country,project_type,maturity_stage,'.implode(',', PrivateInvestmentPreparation::FIELDS), 'declarations.project_title' => 'nullable|string|max:200', 'declarations.project_summary' => 'nullable|string|max:5000',
'declarations.currency' => 'nullable|regex:/^[A-Z]{3}$/', 'declarations.expected_start_date' => 'nullable|date_format:Y-m-d', 'declarations.investor_origin' => 'nullable|in:national,foreign,mixed', 'declarations.person_type' => 'nullable|in:singular,collective,sole_trader,consortium', 'declarations.entity_stage' => 'nullable|in:existing,being_formed', 'declarations.tax_residency_country' => ['nullable', 'string', app(CountryCatalogue::class)->rule()], 'declarations.fiscal_residency_country' => ['nullable', 'string', app(CountryCatalogue::class)->rule()], 'declarations.project_type' => 'nullable|string|max:100', 'declarations.maturity_stage' => 'nullable|string|max:100',
'replace_fields' => 'nullable|array', 'replace_fields.*' => 'string|in:'.implode(',', self::FIELDS)])->validate();
@@ -294,6 +381,7 @@ final class InvestorContext
abort_unless((int) $case->version === $data['version'], 409, 'O dossier mudou; recupere a versão atual.');
app(InvestorPath::class)->view($actor, $id);
$old = $this->state($case);
$complements = array_key_exists('complements', $data) ? app(InvestmentComplements::class)->resolve($data['complements'], $old['complements'] ?? []) : ($old['complements'] ?? []);
$links = $old['preparation_documents'] ?? [];
foreach ($data['document_links'] ?? [] as $key => $document) {
if ($document) {
@@ -303,7 +391,31 @@ final class InvestorContext
unset($links[$key]);
}
}
$choices = $old['opportunity_choices'] ?? [];
if (! array_key_exists('opportunity_choices', $data) && array_key_exists('territory', $data['selection'])) {
$source = $data['selection']['opportunity'] ?? ($old['selection']['opportunity']['key'] ?? null);
if ($source && str_starts_with($source, 'opportunity:')) {
$documented = Opportunity::findOrFail(substr($source, 12))->primary_territory_id;
abort_if($documented && $data['selection']['territory'] !== 'territory:'.$documented, 422,
'Confirme a alternativa territorial por ficha e indique o motivo; a localização documentada foi preservada.');
}
}
if (array_key_exists('opportunity_choices', $data)) {
$choices = app(InvestmentOpportunityChoices::class)->resolve($data['opportunity_choices'], $choices);
$data['selection']['opportunity'] = $choices[0]['opportunity']['key'] ?? null;
// Shared fields concern the first identified opportunity; other sites remain separate.
$data['selection']['territory'] = $choices[0]['territory']['key'] ?? null;
if (array_key_exists('park', $data['opportunity_choices'][0] ?? [])) $data['selection']['park'] = $choices[0]['park']['key'] ?? null;
} elseif (array_key_exists('opportunity', $data['selection']) && $choices !== []) {
abort(422, 'Actualize a selecção identificada de oportunidades; não substitua silenciosamente o conjunto por uma ficha.');
}
$selection = $old['selection'];
foreach ($complements as $complement) {
if (($complement['availability'] ?? '') === 'review_required') continue;
foreach ([$complement['origin']['key'], $complement['target']['key'] ?? ''] as $key) {
if (str_starts_with($key, 'opportunity:')) abort_unless(in_array($key, array_column(array_column($choices, 'opportunity'), 'key'), true), 422, 'Conserve a oportunidade escolhida ou retire explicitamente a sua relação complementar.');
}
}
foreach ($data['selection'] as $slot => $key) {
if ($key === null || $key === '') {
unset($selection[$slot]);
@@ -315,6 +427,16 @@ final class InvestorContext
abort_unless(in_array($ref['kind'], $expected, true), 422, 'Referência incompatível com a dimensão.');
$selection[$slot] = $ref;
}
$corridor = $choices[0]['corridor_key'] ?? (isset($selection['opportunity']) && $selection['opportunity']['kind'] === 'opportunity'
? app(InvestmentCorridorContext::class)->key(Opportunity::findOrFail($selection['opportunity']['id'])) : null);
if ($corridor && isset($selection['territory'])) abort_unless(app(InvestmentCorridorContext::class)->compatible($corridor, $selection['territory']['id']), 422, 'A localização não pertence ao corredor escolhido.');
if ($corridor && isset($selection['park'])) {
$eligible = array_column(app(InvestmentCorridorContext::class)->parks($corridor, $selection['territory']['id'] ?? null), 'id');
if (!in_array($selection['park']['id'], $eligible, true)) {
abort_if(!empty($data['selection']['park']), 422, 'A zona ou parque não tem enquadramento compatível documentado nesta localização.');
unset($selection['park']);
}
}
// A new opportunity may propose its explicitly stored sector; it never matches names.
if (isset($data['selection']['opportunity']) && ! array_key_exists('sector', $data['selection']) && isset($selection['opportunity']['fields']['sector_id'])) {
if (Sector::where('is_active', true)->whereKey($selection['opportunity']['fields']['sector_id'])->exists()) {
@@ -323,7 +445,7 @@ final class InvestorContext
}
$proposed = [];
$origins = [];
foreach (['opportunity', 'sector', 'park', 'territory'] as $slot) {
foreach (['portfolio', 'opportunity', 'sector', 'park', 'territory'] as $slot) {
if (in_array($slot, ['park', 'territory'], true) && array_intersect(array_keys($selection[$slot]['fields'] ?? []), array_keys(InvestmentGeography::FIELDS))) {
foreach (['province_id', 'district_id', 'admin_post_id', 'locality_id', 'province', 'district', 'location'] as $key) {
unset($proposed[$key],$origins[$key]);
@@ -359,22 +481,37 @@ final class InvestorContext
unset($fields[$key],$provenance[$key]);
}
}
if ($corridor) {
foreach (['province_id','district_id','admin_post_id','locality_id','province','district','location'] as $key) {
if (isset($selection['territory']['fields'][$key])) {
$fields[$key]=(string)$selection['territory']['fields'][$key];
$provenance[$key]=Arr::except($selection['territory'],['fields']);
} else unset($fields[$key],$provenance[$key]);
}
$fields['special_zone_id']=$selection['park']['id']??'';
$fields['investment_regime']=$selection['park']['fields']['investment_regime']??'general';
}
$replaced = [];
foreach (array_unique([...array_keys($old['fields']), ...array_keys($fields)]) as $key) {
if (($old['fields'][$key] ?? null) !== ($fields[$key] ?? null)) {
$replaced[] = ['field' => $key, 'before' => $old['fields'][$key] ?? null, 'after' => $fields[$key] ?? null];
}
}
foreach (['opportunity', 'sector', 'territory', 'park'] as $slot) {
foreach (['portfolio', 'opportunity', 'sector', 'territory', 'park'] as $slot) {
if (($old['selection'][$slot] ?? null) !== ($selection[$slot] ?? null)) {
$changed[] = $slot;
}
}
$changedFields = array_column($replaced, 'field');
if ($complements !== ($old['complements'] ?? [])) $changed[] = 'complements';
$choicesChanged = $choices !== ($old['opportunity_choices'] ?? []);
$choiceInputsChanged = array_map(fn($c)=>Arr::except($c,['portfolio_origins']),$choices) !== array_map(fn($c)=>Arr::except($c,['portfolio_origins']),$old['opportunity_choices']??[]);
if ($choiceInputsChanged) $changed = array_values(array_unique([...$changed, 'opportunity', 'territory']));
elseif ($choicesChanged) $changed[] = 'portfolio_origins';
$conditionsChanged = (bool) array_intersect($changedFields, array_diff(self::FIELDS, ['project_title', 'project_summary']));
$dependencyChanged = $this->dependencyHash($old['selection']) !== $this->dependencyHash($selection) || $conditionsChanged;
$dependencyChanged = $choiceInputsChanged || in_array('complements', $changed, true) || $this->dependencyHash($old['selection']) !== $this->dependencyHash($selection) || $conditionsChanged;
$privateConditionsChanged = (bool) array_intersect($changedFields, PrivateInvestmentPreparation::FIELDS);
$calculationInputsChanged = $this->dependencyHash($old['selection']) !== $this->dependencyHash($selection) || (bool) array_intersect($changedFields, array_diff(self::FIELDS, ['project_title', 'project_summary', ...array_diff(PrivateInvestmentPreparation::FIELDS, ['total_investment', 'area_required', 'area_unit'])]));
$calculationInputsChanged = $choiceInputsChanged || $this->dependencyHash($old['selection']) !== $this->dependencyHash($selection) || (bool) array_intersect($changedFields, array_diff(self::FIELDS, ['project_title', 'project_summary', ...array_diff(PrivateInvestmentPreparation::FIELDS, ['total_investment', 'area_required', 'area_unit'])]));
$payload = $this->desk->decode($case->payload);
$results = $payload['investor_path']['results'] ?? [];
$requests = DB::table('desk_requests')->where('case_id', $id)->orderBy('id')->get(['id', 'status', 'protocol', 'version'])->all();
@@ -385,6 +522,7 @@ final class InvestorContext
if ($privateConditionsChanged) {
$steps = array_values(array_unique([...$steps, 'S02', 'S05', 'S06', 'S07']));
}
if (in_array('complements', $changed, true)) $steps = array_values(array_unique([...$steps, 'S02', 'S08']));
$privatePreparation = null;
if (app(PrivateInvestmentPreparation::class)->available()) {
$docs = $this->preparationDocuments($id);
@@ -392,11 +530,12 @@ final class InvestorContext
$afterPreparation = app(PrivateInvestmentPreparation::class)->assess(['fields' => $fields, 'selection' => $selection, 'preparation_documents' => $links], $docs);
$privatePreparation = ['before' => $beforePreparation, 'after' => $afterPreparation];
}
$plan = ['case_id' => $id, 'version' => (int) $case->version, 'revision' => (int) $old['revision'], 'selection' => $selection, 'fields' => $fields, 'provenance' => $provenance,
$plan = ['complements' => $complements, 'opportunity_choices' => $choices, 'case_id' => $id, 'version' => (int) $case->version, 'revision' => (int) $old['revision'], 'selection' => $selection, 'fields' => $fields, 'provenance' => $provenance,
'preparation_documents' => $links, 'private_investment' => $privatePreparation,
'updated' => $replaced, 'maintained' => $kept, 'changed_dimensions' => $changed, 'dependent_results' => (int) $case->user_id === $actor->id && $calculationInputsChanged ? array_map(fn ($r) => ['id' => $r['id'], 'engine' => $r['engine']], $results) : [],
'requests' => $requests, 'review_steps' => $steps, 'dependency_changed' => $dependencyChanged, 'calculation_inputs_changed' => $calculationInputsChanged, 'documents_preserved' => DB::table('desk_documents')->where('case_id', $id)->count(),
'notice' => 'Não confirma incentivos, licenças, disponibilidade de terra, reserva ou autorização. Submissões e decisões anteriores mantêm o conteúdo enviado.'];
$plan['requirements_assessment'] = app(InvestmentContextRequirements::class)->assess(['opportunity_choices' => $choices, 'fields' => $fields]);
$hash = hash_hmac('sha256', json_encode(Arr::sortRecursive([$actor->id, $data, $plan]), JSON_THROW_ON_ERROR), config('app.key'));
return [...$plan, 'preview_hash' => $hash];
@@ -435,14 +574,17 @@ final class InvestorContext
foreach ($plan['review_steps'] as $step) {
$review[$step] = ['revision' => $revision, 'reason' => 'O contexto mudou; reveja os pressupostos e a evidência aplicável.'];
}
$context = ['preparation_documents' => $plan['preparation_documents'], 'preparation_snapshot' => $plan['private_investment']['after'] ?? $before['preparation_snapshot'] ?? null, 'revision' => $revision, 'selection' => $plan['selection'], 'fields' => $plan['fields'], 'provenance' => $plan['provenance'], 'review_steps' => $review,
'dependency_hash' => $this->dependencyHash(['selection' => $plan['selection'], 'fields' => Arr::except($plan['fields'], ['project_title', 'project_summary'])]), 'updated_at' => now()->toIso8601String(), 'updated_by' => $actor->id];
$context = ['opportunity_choices' => $plan['opportunity_choices'], 'preparation_documents' => $plan['preparation_documents'], 'preparation_snapshot' => $plan['private_investment']['after'] ?? $before['preparation_snapshot'] ?? null, 'revision' => $revision, 'selection' => $plan['selection'], 'fields' => $plan['fields'], 'provenance' => $plan['provenance'], 'review_steps' => $review,
'dependency_hash' => $this->dependencyHash([...($plan['opportunity_choices'] !== [] ? ['opportunity_choices' => $plan['opportunity_choices']] : []), ...($plan['complements'] ? ['complements' => $plan['complements']] : []), 'selection' => $plan['selection'], 'fields' => Arr::except($plan['fields'], ['project_title', 'project_summary'])]), 'updated_at' => now()->toIso8601String(), 'updated_by' => $actor->id];
$context['requirements_assessment'] = $plan['requirements_assessment'];
$context['complements'] = $plan['complements'];
if ($plan['calculation_inputs_changed']) {
foreach ($payload['investor_path']['results'] ?? [] as $i => $r) {
$payload['investor_path']['results'][$i] = [...$r, 'context_state' => 'review_required', 'context_invalidated_revision' => $revision, 'context_invalidated_at' => now()->toIso8601String()];
}
}
$payload['investor_path']['context'] = $context;
unset($payload['investor_path']['context_drafts'][(string)$actor->id]);
DB::table('desk_cases')->where('id', $id)->update(['payload' => $this->desk->encode($payload), 'title' => $context['fields']['project_title'] ?? $case->title, 'version' => $case->version + 1, 'updated_at' => now()]);
$this->desk->event($this->desk->case($actor, $id), $actor, 'investment_context_changed', ['before' => $before, 'after' => $context, 'affected_result_ids' => array_column($plan['dependent_results'], 'id'), 'preserved_request_ids' => array_column($plan['requests'], 'id')]);
$result = ['case_id' => $id, 'version' => (int) $case->version + 1, 'revision' => $revision, 'replayed' => false];
@@ -452,12 +594,13 @@ final class InvestorContext
}, 3);
}
public function view(User $actor, string $id): array
public function view(User $actor, string $id, ?string $opportunity = null): array
{
$case = $this->desk->case($actor, $id);
abort_unless($this->desk->owns($actor, $case, false) || $actor->can('manage-portal'), 403);
$ctx = $this->state($case);
$path = app(InvestorPath::class)->view($actor, $id);
$ctx['land_preparation'] = $this->landPreparation($case, $opportunity);
$path = app(InvestorPath::class)->view($actor, $id, $opportunity);
$ctx['path_applicability'] = ['definition_id' => $path['definition_id'], 'definition_version' => $path['definition_version'], 'source_sha256' => $path['source_sha256'],
'steps' => array_values(array_map(fn ($step) => Arr::only($step, ['code', 'title', 'applicability', 'reason', 'state', 'context_review_required']), array_filter($path['steps'], fn ($step) => ! empty($step['branch']))))];
@@ -472,12 +615,19 @@ final class InvestorContext
}
}
$preparation = app(PrivateInvestmentPreparation::class)->available() ? app(PrivateInvestmentPreparation::class)->assess($ctx, $this->preparationDocuments($id)) : null;
$preparation = app(PrivateInvestmentPreparation::class)->available() ? app(PrivateInvestmentPreparation::class)->assess(app(InvestmentConsumerScope::class)->context($ctx, $opportunity), $this->preparationDocuments($id)) : null;
$ctx['preparation_assessment'] = $preparation;
$prefill = $this->prefill($ctx, $opportunity);
$saved = $this->desk->decode($case->payload);
if (!empty($saved['reuse_consent'])) {
$contacts = array_filter(['email' => $saved['email'] ?? null, 'phone' => $saved['profile']['phone'] ?? null], fn($v) => is_string($v) && $v !== '');
$prefill['simulator'] = [...$contacts, ...$prefill['simulator']];
$prefill['land'] = [...$contacts, ...(!empty($saved['name']) ? ['full_name' => $saved['name']] : []), ...$prefill['land']];
}
return ['private_investment' => $preparation, 'preparation_documents' => $this->preparationDocuments($id), 'simulator_context_revision' => $simulatorRevision, 'case' => $path['case'], 'context' => $ctx, 'steps' => array_map(fn ($s) => Arr::only($s, ['code', 'title', 'state', 'context_review_required']), $path['steps']),
'can_write' => $path['canWrite'], 'next' => $path['next'], 'results' => $path['results'], 'requests' => $path['requests'],
'prefill' => $this->prefill($ctx), 'notice' => 'A seleção apoia a preparação e não confirma benefícios, licenças, direitos ou disponibilidade.'];
'prefill' => $prefill, 'notice' => 'A seleção apoia a preparação e não confirma benefícios, licenças, direitos ou disponibilidade.'];
}
public function preparationDocuments(string $id): array
@@ -485,21 +635,57 @@ final class InvestorContext
return DB::table('desk_documents as l')->join('documents as d', 'd.id', '=', 'l.document_id')->where('l.case_id', $id)->whereNull('d.deleted_at')->get(['l.id', 'l.document_id', 'd.file_name', 'd.status', 'd.malware_scan_status', 'd.version_no', 'd.sha256'])->map(fn ($row) => (array) $row)->all();
}
public function prefill(array $ctx): array
public function landPreparation(\stdClass $case, ?string $opportunity = null): array
{
$payload = $this->desk->decode($case->payload);
$ctx = $this->state($case);
$key = app(InvestmentConsumerScope::class)->key($ctx, $opportunity);
$path = $payload['investor_path'] ?? [];
$need = app(InvestmentConsumerScope::class)->facts($payload, $ctx, $key)['land_needed'];
return ['need' => in_array($need, ['yes', 'no'], true) ? $need : 'unknown', 'reason' => $key ? ($path['opportunity_fact_reasons'][$key]['land_needed'] ?? '') : ($path['fact_reasons']['land_needed'] ?? '')];
}
public function prefill(array $ctx, ?string $opportunity = null): array
{
$original = $ctx;
$ctx = app(InvestmentConsumerScope::class)->context($ctx, $opportunity);
$f = $ctx['fields'];
$sim = Arr::except(Arr::only($f, self::FIELDS), ['sector', 'province', 'district', 'location', ...array_diff(PrivateInvestmentPreparation::FIELDS, ['total_investment', 'area_required', 'area_unit'])]);
// Entering this tool from an investment dossier selects the existing investment-analysis route.
// The reconciliation preserves any purpose deliberately chosen in an existing simulator draft.
$sim += ['journey_type' => 'investor', 'journey_type_label' => 'Avaliar o meu investimento', 'purpose' => 'simulate_only', 'purpose_label' => 'Apenas simular'];
if (($ctx['land_preparation']['need'] ?? null) === 'yes') {
$tenure = ['duat' => ['duat', 'DUAT'], 'lease' => ['leased', 'Arrendado']][$f['site_tenure'] ?? ''] ?? null;
if ($tenure) [$sim['land_status'], $sim['land_status_label']] = $tenure;
}
// Human labels travel with the exact existing identifiers, not inferred locations.
foreach (app(InvestmentGeography::class)->publicReferences()->whereIn('id', array_values(Arr::only($f, array_keys(InvestmentGeography::FIELDS))))->get(['id', 'name']) as $place) {
foreach (array_keys(InvestmentGeography::FIELDS) as $key) {
if (($f[$key] ?? null) === $place->id) $sim[$key.'_label'] = $place->name;
}
}
if (! empty($f['sector_id']) && ($sector = Sector::where('is_active', true)->find($f['sector_id']))) $sim['sector_id_label'] = $sector->name;
$zone = $ctx['selection']['park'] ?? null;
$sim['context_applicability_snapshot'] = json_encode([
'version' => 1, 'context_revision' => $ctx['revision'],
'consumer_opportunity' => $ctx['consumer_opportunity']['opportunity']['key'] ?? null,
'portfolio_origin' => $ctx['selection']['portfolio'] ?? null,
'zone' => $zone ? Arr::only($zone, ['id', 'title', 'href', 'version', 'sha256', 'fields', 'analysis']) : null,
'provenance' => $ctx['provenance'],
'opportunity_choices' => $ctx['opportunity_choices'] ?? [],
'complements' => $ctx['complements'] ?? [],
'requirements_assessment' => $ctx['requirements_assessment'] ?? null,
'path' => $ctx['path_applicability'] ?? null,
'land_preparation' => $ctx['land_preparation'] ?? null,
'field_provenance' => array_map(fn($origin) => Arr::only($origin, ['kind', 'title', 'version', 'context_revision']), $ctx['provenance'] ?? []),
'preparation' => isset($ctx['preparation_assessment']) ? Arr::except($ctx['preparation_assessment'], ['questions']) : null,
'fields' => Arr::only($f, ['investor_origin', 'tax_residency_country', 'entity_stage', 'currency', 'expected_start_date', 'investment_regime']),
'notice' => 'Referências e declarações para preparação; sem reconhecimento automático de elegibilidade ou benefício.',
], JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
$scoped = app(InvestmentConsumerScope::class)->context($original, $opportunity);
$f = $scoped['fields'];
return ['simulator' => $sim, 'land' => array_filter(['project_reference' => $f['project_title'] ?? null, 'activity' => $f['sector'] ?? null, 'purpose' => $f['project_summary'] ?? null, 'parcels' => [Arr::only($f, array_keys(InvestmentGeography::FIELDS))]], fn ($v) => $v !== null),
'desk' => array_filter(['title' => $f['project_title'] ?? null, 'territory_id' => $f['locality_id'] ?? $f['admin_post_id'] ?? $f['district_id'] ?? $f['province_id'] ?? null, 'location_source' => isset($f['province_id']) ? 'Referência consultada no contexto v'.($ctx['revision'] ?? 0).': '.($f['location'] ?? 'território identificado').'. Não confirma disponibilidade.' : null, 'description' => $f['project_summary'] ?? null, 'sector' => $f['sector'] ?? null, ...Arr::only($f, array_keys(InvestmentGeography::FIELDS))], fn ($v) => $v !== null),
'submission' => array_filter(['currency' => $f['currency'] ?? null, 'sector' => $f['sector'] ?? null, 'objective' => $f['project_summary'] ?? null, 'location' => $f['location'] ?? null], fn ($v) => $v !== null)];
+38 -16
View File
@@ -68,7 +68,7 @@ final class InvestorPath
}
/** @return array<string,mixed> */
public function view(User $actor, string $id): array
public function view(User $actor, string $id, ?string $opportunity = null): array
{
abort_unless($this->available(), 404);
$case = $this->desk->case($actor, $id);
@@ -79,18 +79,26 @@ final class InvestorPath
$payload = $this->desk->decode($case->payload);
$path = $payload['investor_path'] ?? [];
$context = app(InvestorContext::class)->state($case);
$facts = $path['facts'] ?? ['land_needed' => 'unknown', 'external_funding' => 'unknown', 'viability' => 'unknown'];
$scope = app(InvestmentConsumerScope::class);
$opportunity = $scope->key($context, $opportunity);
$allChoices = $context['opportunity_choices'] ?? [];
$facts = $scope->facts($payload, $context, $opportunity);
$factReasons = $opportunity ? ($path['opportunity_fact_reasons'][$opportunity] ?? []) : ($path['fact_reasons'] ?? []);
$context = $scope->context($context, $opportunity);
$records = DB::table('journey_steps')->where('journey_id', $journey->id)->whereNull('deleted_at')->get()->keyBy('step_code');
$steps = [];
foreach ($definition['steps'] as $step) {
$row = $records->get($step['code']);
abort_unless($row !== null, 409, 'Etapa em falta; reparar a instalação sem reconstruir o histórico.');
$detail = $this->desk->decode($row->path_detail);
if ($opportunity && (count($allChoices) > 1 || isset($detail['opportunities'][$opportunity]))) {
$detail = $detail['opportunities'][$opportunity] ?? ['state' => 'not_started'];
}
$value = $step['branch'] ? ($facts[$step['branch']] ?? 'unknown') : 'yes';
$applicable = $value === 'unknown' ? 'undetermined' : ($value === 'no' ? 'not_needed_for_preparation' : 'applicable');
$state = $value === 'no' ? 'dispensed' : $detail['state'];
$steps[] = [...$step, 'id' => $row->id, 'version' => $row->version_no, 'state' => $state, 'saved_state' => $detail['state'],
'applicability' => $applicable, 'reason' => $value === 'no' ? ($path['fact_reasons'][$step['branch']] ?? '') : ($detail['reason'] ?? ''),
'applicability' => $applicable, 'reason' => $value === 'no' ? ($factReasons[$step['branch']] ?? '') : ($detail['reason'] ?? ''),
'detail' => $detail, 'preparation_allowed' => $value !== 'no',
'action_gaps' => []];
}
@@ -143,7 +151,8 @@ final class InvestorPath
$landCase = (int) $case->user_id === $actor->id
? LandAssistanceCase::where('user_id', $actor->id)->latest('updated_at')->get()
->first(fn ($land) => ($land->payload['investor_path_case_id'] ?? null) === $id) : null;
->first(fn ($land) => ($land->payload['investor_path_case_id'] ?? null) === $id
&& (!$allChoices || ($opportunity && ($land->payload['context_opportunity'] ?? null) === $opportunity))) : null;
$preparation = ($owner || $manager) && app(PrivateInvestmentPreparation::class)->available() ? app(PrivateInvestmentPreparation::class)->assess($context, $documents->map(fn ($d) => (array) $d)->all()) : null;
if ($preparation && $preparation['enabled']) {
@@ -152,11 +161,12 @@ final class InvestorPath
return ['private_investment' => $preparation, 'case' => ['id' => $id, 'title' => $case->title, 'version' => $case->version, 'closed' => $case->closed_at !== null],
'journey_id' => $journey->id, 'definition_id' => $version->id, 'definition_version' => $version->version_no, 'source_sha256' => $definition['source_sha256'],
'phases' => $definition['phases'], 'steps' => $steps, 'facts' => $facts, 'fact_reasons' => $path['fact_reasons'] ?? [],
'phases' => $definition['phases'], 'steps' => $steps, 'facts' => $facts, 'fact_reasons' => $factReasons,
'context_opportunity' => $opportunity, 'opportunity_options' => $allChoices,
'fintech' => ($owner || $manager) && (isset($path['fintech']) || collect($path['sector_contexts'] ?? [])->contains(fn ($s) => in_array($s['slug'], ['fintech', 'tecnologias-de-informacao', 'mercado-financeiro'], true)) || collect($path['content_sources'] ?? [])->contains('return_href', '/tech-fintech')) ? app(FintechInvestment::class)->view($payload, $steps) : null,
'canWrite' => $owner && ! $case->closed_at, 'canReview' => $manager && (int) $case->user_id !== $actor->id && ! $case->closed_at,
'next' => $next ? ['code' => $next['code'], 'title' => $next['title'], 'action' => 'Preparar', 'href' => $next['href'] ?? '#'.$next['code']] : null,
'investment_context' => ($owner || $manager) ? $context : null,
'investment_context' => ($owner || $manager) ? [...$context, 'opportunity_choices' => $opportunity ? $context['opportunity_choices'] : $allChoices] : null,
'results' => $results, 'requests' => $requests, 'documents' => $documents,
'zone_contexts' => ($owner || $manager) ? ($path['zone_contexts'] ?? []) : [],
'sector_contexts' => ($owner || $manager) ? ($path['sector_contexts'] ?? []) : [],
@@ -167,25 +177,31 @@ final class InvestorPath
->get(['id', 'title', 'status', 'version_no', 'due_at', 'assignee_user_id']),
'services' => DB::table('desk_service_versions')->where('status', 'published')->orderByDesc('version')->get(['id', 'code', 'definition'])
->unique('code')->values()->map(fn ($s) => ['id' => $s->id, 'title' => json_decode($s->definition, true)['title']]),
'tools' => ['simulator' => (int) $case->user_id === $actor->id ? '/simulador-fiscal?context='.$id.($case->simulation_id ? '&draft='.$case->simulation_id : '') : null,
'desk' => '/balcao-virtual/dossiers/'.$id, 'guidance' => '/balcao-virtual/dossiers/'.$id.'/orientacao',
'tools' => ['simulator' => (int) $case->user_id === $actor->id ? '/simulador-fiscal?context='.$id.($case->simulation_id ? '&draft='.$case->simulation_id : '').($opportunity ? '&opportunity='.urlencode($opportunity) : '') : null,
'desk' => '/balcao-virtual/dossiers/'.$id.($opportunity ? '?opportunity='.urlencode($opportunity) : ''), 'guidance' => '/balcao-virtual/dossiers/'.$id.'/orientacao',
'library' => '/documentos?context='.$id, 'opportunities' => '/oportunidades?context='.$id, 'territory' => '/zonas?context='.$id, 'partners' => '/parcerias/painel?context='.$id,
'land' => (int) $case->user_id === $actor->id ? ($landCase ? '/investidor/terra-duat/'.$landCase->id : '/investidor/terra-duat/novo?context='.$id) : null]];
'land' => ($facts['land_needed'] ?? 'unknown') !== 'no' && (int) $case->user_id === $actor->id ? ($landCase ? '/investidor/terra-duat/'.$landCase->id : '/investidor/terra-duat/novo?context='.$id.($opportunity ? '&opportunity='.urlencode($opportunity) : '')) : null]];
}
/** @param array<string,mixed> $input */
public function facts(User $actor, string $id, array $input): void
{
$data = Validator::make($input, ['version' => 'required|integer|min:1',
$data = Validator::make($input, ['version' => 'required|integer|min:1', 'context_opportunity' => 'nullable|string|max:100',
'land_needed' => ['required', Rule::in(['yes', 'no', 'unknown'])], 'external_funding' => ['required', Rule::in(['yes', 'no', 'unknown'])],
'viability' => ['required', Rule::in(['unknown', 'proceed', 'reformulate', 'archive'])],
'reason' => 'required|string|min:3|max:3000'])->validate();
DB::transaction(function () use ($actor, $id, $data): void {
$case = $this->writable($actor, $id, $data['version']);
$payload = $this->desk->decode($case->payload);
$choice = app(InvestmentConsumerScope::class)->key(app(InvestorContext::class)->state($case), $data['context_opportunity'] ?? null, true);
foreach (['land_needed', 'external_funding', 'viability'] as $key) {
$payload['investor_path']['facts'][$key] = $data[$key];
$payload['investor_path']['fact_reasons'][$key] = $data['reason'];
if ($choice) {
$payload['investor_path']['opportunity_facts'][$choice][$key] = $data[$key];
$payload['investor_path']['opportunity_fact_reasons'][$choice][$key] = $data['reason'];
} else {
$payload['investor_path']['facts'][$key] = $data[$key];
$payload['investor_path']['fact_reasons'][$key] = $data['reason'];
}
}
$this->savePayload($case, $payload);
$this->desk->event($this->desk->case($actor, $id), $actor, 'path_applicability_declared', $data);
@@ -195,7 +211,7 @@ final class InvestorPath
/** @param array<string,mixed> $input */
public function stage(User $actor, string $id, string $code, array $input): void
{
$data = Validator::make($input, ['version' => 'required|integer|min:1', 'state' => ['required', Rule::in(array_diff(InvestorPathDefinition::STATES, ['dispensed']))],
$data = Validator::make($input, ['version' => 'required|integer|min:1', 'context_opportunity' => 'nullable|string|max:100', 'state' => ['required', Rule::in(array_diff(InvestorPathDefinition::STATES, ['dispensed']))],
'reason' => 'required|string|min:3|max:3000', 'action' => ['required', Rule::in(['prepare', 'submit', 'execute', 'operate', 'review', 'record_external'])],
'document_id' => 'nullable|uuid', 'authority' => 'nullable|string|max:200', 'reference' => 'nullable|string|max:300',
'context_revision' => 'nullable|integer|min:0', 'context_reviewed' => 'nullable|boolean', 'decision_date' => 'nullable|date_format:Y-m-d|before_or_equal:today'])->validate();
@@ -203,7 +219,8 @@ final class InvestorPath
$case = $this->desk->case($actor, $id, false, true);
abort_if($case->closed_at, 409);
abort_unless((int) $case->version === $data['version'], 409, 'O dossier mudou; recupere a versão actual.');
$view = $this->view($actor, $id);
$key = app(InvestmentConsumerScope::class)->key(app(InvestorContext::class)->state($case), $data['context_opportunity'] ?? null, true);
$view = $this->view($actor, $id, $key);
$step = $this->findStep($view, $code);
abort_unless($step !== null && $step['preparation_allowed'], 422, 'Etapa dispensada de preparação; altere os pressupostos com fundamento.');
$state = $data['state'];
@@ -238,7 +255,10 @@ final class InvestorPath
}
abort_if($state === 'completed' && ($step['context_review_required'] ?? false) && (! (bool) ($data['context_reviewed'] ?? false) || ($data['context_revision'] ?? -1) !== ($view['investment_context']['revision'] ?? 0)), 422, 'O contexto desta etapa exige nova conferência fundamentada; o registo anterior permanece no histórico.');
$detail = [...$data, 'context_dependency_hash' => $view['investment_context']['dependency_hash'] ?? null, 'fintech_fingerprint' => $view['fintech']['review_fingerprints'][$code] ?? null, 'scope' => $scope, 'actor_id' => $actor->id, 'recorded_at' => now()->toIso8601String()];
DB::table('journey_steps')->where('id', $step['id'])->update(['path_detail' => $this->desk->encode($detail),
$stored = $this->desk->decode(DB::table('journey_steps')->where('id', $step['id'])->value('path_detail'));
if ($key) $stored['opportunities'][$key] = $detail;
else $stored = [...$stored, ...$detail];
DB::table('journey_steps')->where('id', $step['id'])->update(['path_detail' => $this->desk->encode($stored),
'status' => in_array($state, ['completed', 'approved'], true) ? 'completed' : ($state === 'not_started' ? 'pending' : 'active'),
'version_no' => $step['version'] + 1, 'updated_at' => now()]);
DB::table('desk_cases')->where('id', $id)->update(['version' => $case->version + 1, 'updated_at' => now()]);
@@ -267,11 +287,12 @@ final class InvestorPath
abort_unless(is_array($parsed) && ($parsed['version'] ?? null) === 1 && is_array($parsed['data'] ?? null), 422, 'Snapshot financeiro incompatível.');
$payload = $this->desk->decode($case->payload);
$context = app(InvestorContext::class)->state($case);
$opportunity = app(InvestmentConsumerScope::class)->key($context, $economic['context_opportunity'] ?? null, true);
if ($context['revision'] > 0) {
abort_unless(($data['context_revision'] ?? -1) === $context['revision'], 409, 'O contexto mudou; recupere e confira os pressupostos antes de devolver o resultado.');
abort_unless($data['context_reviewed'] ?? false, 422, 'Confirme a revisão dos pressupostos financeiros para este contexto.');
}
$resultKey = hash('sha256', $draft->id.':'.$draft->version_no.':'.$snapshot.':context:'.$context['revision']);
$resultKey = hash('sha256', $draft->id.':'.$draft->version_no.':'.$snapshot.':context:'.$context['revision'].($opportunity ? ':'.$opportunity : ''));
foreach ($payload['investor_path']['results'] ?? [] as $result) {
if ($result['source_key'] === $resultKey) {
return $result;
@@ -280,6 +301,7 @@ final class InvestorPath
abort_unless((int) $case->version === $data['version'], 409, 'Recupere a versão actual do dossier.');
$result = ['id' => (string) Str::uuid(), 'source_key' => $resultKey, 'draft_id' => $draft->id, 'draft_version' => $draft->version_no,
'snapshot_sha256' => $draft->snapshot_sha256, 'financial_snapshot' => $snapshot,
'context_opportunity' => $opportunity,
'currency' => $economic['currency'] ?? null, 'engine' => 'financial-calculators:v1',
'kind' => 'local_calculation_inputs', 'context_state' => 'current', 'context_revision' => $context['revision'], 'context_dependency_hash' => $context['dependency_hash'] ?? app(InvestorContext::class)->dependencyHash([]), 'recorded_at' => now()->toIso8601String()];
$payload['investor_path']['results'][] = $result;
+6 -3
View File
@@ -78,6 +78,9 @@ final class LegacyContent
$image = $this->publicResources()->where('r.id', $adaptation['image_resource_id'])->whereIn('d.media_type', ['image/png', 'image/jpeg', 'image/webp'])->first();
}
}
if ($slug === 'agronegocio') {
$localHtml = app(CorridorAgribusiness::class)->enrich($localHtml, app(CorridorAgribusinessOpportunities::class)->articleLinks());
}
preg_match_all('~href="([^"]+)"~i', $localHtml, $existingLinks);
$alreadyLinked = array_map(fn ($href) => html_entity_decode($href, ENT_QUOTES | ENT_HTML5, 'UTF-8'), $existingLinks[1]);
$relatedArticles = [];
@@ -279,7 +282,7 @@ final class LegacyContent
}
/** Allow presentation markup only. No scripts, styles, events, embeds or remote image execution. */
public function sanitize(string $html): string
public function sanitize(string $html, bool $preserveStructure = false): string
{
$doc = new DOMDocument;
$old = libxml_use_internal_errors(true);
@@ -296,7 +299,7 @@ final class LegacyContent
}
$headingOffset = 2 - (empty($sourceLevels) ? 2 : min($sourceLevels));
$previousHeading = 1;
$clean = function (DOMNode $node) use (&$clean, $doc, $headingOffset, &$previousHeading): void {
$clean = function (DOMNode $node) use (&$clean, $doc, $headingOffset, &$previousHeading, $preserveStructure): void {
foreach (iterator_to_array($node->childNodes) as $child) {
if (! $child instanceof DOMElement) {
continue;
@@ -311,7 +314,7 @@ final class LegacyContent
$node->replaceChild($section, $child);
$child = $section;
}
if (! in_array(strtolower($child->tagName), ['p', 'strong', 'b', 'em', 'i', 'ul', 'ol', 'li', 'br', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'a'], true)) {
if (! in_array(strtolower($child->tagName), array_merge(['p', 'strong', 'b', 'em', 'i', 'ul', 'ol', 'li', 'br', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'a'], $preserveStructure ? ['div', 'span', 'small', 'section', 'table', 'caption', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'figure', 'figcaption'] : []), true)) {
$node->removeChild($child);
continue;
@@ -0,0 +1,55 @@
<?php
namespace App\Services;
use App\Models\Opportunity;
/** Read-only grouping of the existing public catalogue. */
final class OpportunityCorridorExplorer
{
public function groups(array $filters): array
{
$catalogue = app(InvestmentCatalogue::class);
$groups = [];
foreach ($catalogue->query($filters)->get() as $record) {
$context = $catalogue->context($record->id);
$classification = $context['classification'] ?? [];
$corridors = collect($context['corridors'])->mapWithKeys(fn ($c) => ['corridor:'.$c->id => $c->name])->all();
foreach ($classification as $relation) {
if ($relation['target_type'] === 'corridor' && ! in_array($relation['purpose'], ['proximity', 'administrative_boundary'], true)) {
$corridors['corridor:'.$relation['target_id']] = $relation['label'];
}
}
// The approved source names are not aliases for similarly named master records.
$source = data_get($record->workflow_context, 'corridor_agribusiness');
$chain = null;
if (is_array($source) && ($source['source_sha256'] ?? null) === CorridorAgribusinessOpportunities::SOURCE_HASH) {
$corridors['source:'.hash('sha256', $source['corridor'])] = $source['corridor'];
$chain = $source['chain'];
}
$chains = collect($classification)->where('target_type', 'chain')->pluck('label')->all();
if ($chain) $chains[] = $chain;
$stages = collect($classification)->pluck('production_stage')->filter()->unique()->values()->all();
// Keep the source's actual activity description rather than inventing stage labels.
if ($stages === [] && $chain && ! empty($source['summary'])) $stages = [$source['summary']];
$path = collect($context['primary_territory_path'] ?? []);
$province = $path->firstWhere('location_type', 'province')->name ?? $record->province;
$district = $path->firstWhere('location_type', 'district')->name ?? $record->district;
$item = ['id' => $record->id, 'title' => $record->title, 'slug' => $record->slug, 'summary' => $record->summary,
'province' => $province, 'district' => $district, 'chains' => array_values(array_unique($chains)),
'stages' => $stages, 'maturity_label' => $record->maturity_label];
foreach ($corridors ?: ['other' => 'Outras localizações'] as $key => $name) {
$groups[$key] ??= ['id' => $key, 'name' => $name, 'opportunities' => []];
$groups[$key]['opportunities'][$record->id] = $item;
}
}
foreach ($groups as &$group) {
$group['opportunities'] = array_values($group['opportunities']);
$group['count'] = count($group['opportunities']);
$group['locations'] = array_values(array_unique(array_filter(array_map(fn ($r) => implode(' / ', array_filter([$r['province'], $r['district']])), $group['opportunities']))));
}
unset($group);
uasort($groups, fn ($a, $b) => ($a['id'] === 'other' ? 1 : 0) <=> ($b['id'] === 'other' ? 1 : 0) ?: strnatcasecmp($a['name'], $b['name']));
return array_values($groups);
}
}
+3
View File
@@ -27,6 +27,9 @@ class OpportunityManagement
public function values(Opportunity $record): array
{
$base = $record->only(self::FIELDS);
if (data_get($record->workflow_context, 'publication.purpose') === OpportunityPromotion::PURPOSE) {
$base = [...$base, ...($record->workflow_context['promotional_private_base'] ?? [])];
}
$draft = $record->management_draft ? json_decode($record->management_draft, true, 512, JSON_THROW_ON_ERROR) : [];
return [...$base, ...($this->closed($record) ? $draft : array_intersect_key($draft, array_flip(self::PRIVATE_DRAFT_FIELDS)))];
+168
View File
@@ -0,0 +1,168 @@
<?php
namespace App\Services;
use App\Models\Opportunity;
use App\Models\User;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
/** Tempo's promotional disclosure, independent of investment execution approval. */
final class OpportunityPromotion
{
public const PURPOSE = 'promotional_preparation';
public const NOTICE = 'Em preparação. A divulgação não confirma financiamento, disponibilidade de terreno, licenças ou autorização de execução do investimento.';
public function allowed(User $actor, Opportunity $record): bool
{
$access = app(PortalProfileAccess::class);
if ($record->trashed() || $record->owner_user_id === null || ! $access->canView($actor, $record)
|| $access->has($actor, 'auditor') || in_array($record->status, ['archived', 'rejected'], true)) return false;
// Never overwrite a normal approved publication or its workflow decisions.
if ($record->published_at && data_get($record->workflow_context, 'publication.purpose') !== self::PURPOSE) return false;
$internal = $access->internalAssignment($actor, $record->opportunity_category);
return ($internal && $access->promoterRecordScope($actor, $record) && $access->operational($actor, 'edit', $record->id))
|| ($access->scoped($actor, 'publisher', $record->organization_id, $record->opportunity_category)
&& $access->operational($actor, 'publish', $record->id));
}
private function authorize(User $actor, Opportunity $record): void
{
abort_unless($this->allowed($actor, $record), 403, 'A designação e o âmbito existentes não autorizam esta divulgação.');
if (! app(PortalProfileAccess::class)->mfa($actor)) {
throw new \Illuminate\Http\Exceptions\HttpResponseException(response()->json(['message' => 'Confirme a entrada normal com MFA recente para divulgar.'], 403)->header('X-Portal-Reauthentication', 'required'));
}
}
public function editor(User $actor, Opportunity $record): array
{
abort_unless($this->allowed($actor, $record), 403);
$saved = data_get($record->workflow_context, 'promotional_summary', []);
// Only previously public text is recovered automatically. No private contact/document is copied.
$existing = data_get($record->workflow_context, 'publication.purpose') === self::PURPOSE;
$form = $saved['input'] ?? ['title' => $existing ? $record->title : '', 'summary' => $existing ? $record->summary : '',
'description' => $existing ? $record->description : '', 'source_reference' => '', 'edition' => '', 'source_date' => '',
'limitations' => '', 'territory_ids' => $existing ? DB::table('opportunity_locations')->where('opportunity_id', $record->id)->pluck('geo_location_id')->all() : [],
'sector_id' => $record->sector_id];
return ['record' => ['id' => $record->id, 'reference' => $record->reference], 'etag' => app(OpportunityManagement::class)->etag($record),
'initial' => $form, 'publicNow' => $existing && $record->status === 'published',
'reauthenticationRequired' => ! app(PortalProfileAccess::class)->mfa($actor),
'sectors' => \App\Models\Sector::catalog()->orderBy('name')->get(['id', 'name']),
'territories' => app(InvestmentGeography::class)->publicReferences()->orderBy('name')->get(['id', 'name', 'location_type']),
'history' => DB::table('audit_events')->where('source_system', 'tempo-preparation-publication')
->where('after_data->record_id', $record->id)->orderByDesc('occurred_at')->get(['id', 'action', 'actor_user_id', 'occurred_at'])];
}
public function preview(User $actor, Opportunity $record, array $input): array
{
$this->authorize($actor, $record);
$data = Validator::make($input, ['etag' => 'required|string|size:64', 'action' => 'required|in:disclose,withdraw',
'title' => 'required_if:action,disclose|nullable|string|max:255', 'summary' => 'required_if:action,disclose|nullable|string|max:499',
'description' => 'nullable|string|max:6000', 'limitations' => 'nullable|string|max:2500',
'source_reference' => 'nullable|string|max:2000', 'edition' => 'nullable|string|max:150', 'source_date' => 'nullable|date_format:Y-m-d',
'sector_id' => ['nullable', 'integer', Rule::in(\App\Models\Sector::catalog()->pluck('id')->all())],
'territory_ids' => 'sometimes|array|max:30', 'territory_ids.*' => 'required|uuid|distinct',
])->validate();
abort_unless(hash_equals(app(OpportunityManagement::class)->etag($record), $data['etag']), 409, 'A ficha mudou. Reabra antes de preparar outra prévia.');
if ($data['action'] === 'withdraw') abort_unless(data_get($record->workflow_context, 'publication.purpose') === self::PURPOSE, 409);
$paths = [];
foreach ($data['territory_ids'] ?? [] as $id) {
$path = app(InvestmentGeography::class)->path($id, true);
abort_unless(count($path) > 0 && collect($path)->contains('id', $id), 422, 'Seleccione apenas territórios públicos existentes.');
$paths[] = $path;
}
$text = fn ($key) => trim(strip_tags($data[$key] ?? ''));
$body = implode("\n\n", array_filter([$text('description'), self::NOTICE,
'Limitações e lacunas: '.($text('limitations') ?: 'Informação ainda por completar.'),
'Fonte: '.($text('source_reference') ?: 'Não indicada.'), 'Edição: '.($text('edition') ?: 'Não indicada.'),
'Data-base: '.($data['source_date'] ?? 'Não indicada.')]));
$body = OpportunityPublicText::description($record, $body);
$projection = ['title' => $text('title'), 'summary' => $text('summary'), 'description' => $body,
'sector_id' => $data['sector_id'] ?? null, 'maturity_label' => 'Em preparação', 'notice' => self::NOTICE,
'territories' => array_map(fn ($p) => ['id' => end($p)->id, 'label' => implode(' / ', array_column($p, 'name'))], $paths)];
if ($data['action'] === 'disclose') abort_unless($projection['title'] !== '' && $projection['summary'] !== '', 422, 'Indique título e resumo em texto simples.');
if ($data['action'] === 'withdraw') $projection = [...$projection, ...$record->only(['title', 'summary', 'description', 'sector_id', 'maturity_label'])];
$payload = ['actor_id' => $actor->id, 'record_id' => $record->id, 'etag' => $data['etag'], 'action' => $data['action'],
'input' => $data, 'projection' => $projection, 'expires' => now()->addMinutes(15)->timestamp];
return ['action' => $data['action'], 'projection' => $projection, 'token' => Crypt::encryptString(json_encode($payload, JSON_THROW_ON_ERROR)),
'current' => $record->only(['title', 'summary', 'description', 'status']), 'writes' => false];
}
public function confirm(User $actor, int $id, array $input): array
{
$data = Validator::make($input, ['token' => 'required|string|max:300000', 'operation_key' => 'required|uuid',
'confirm' => 'required|accepted', 'reason' => 'required|string|min:10|max:1000'])->validate();
try { $preview = json_decode(Crypt::decryptString($data['token']), true, flags: JSON_THROW_ON_ERROR); }
catch (\Throwable) { abort(422, 'Prévia inválida. Prepare outra prévia.'); }
abort_unless(($preview['actor_id'] ?? null) === $actor->id && ($preview['record_id'] ?? null) === $id, 403);
return DB::transaction(function () use ($actor, $id, $data, $preview) {
DB::select('SELECT pg_advisory_xact_lock(hashtext(?))', ['promotional-operation:'.$data['operation_key']]);
$record = Opportunity::whereKey($id)->lockForUpdate()->firstOrFail();
$this->authorize($actor, $record);
$digest = hash('sha256', $data['token'].'|'.$data['reason']);
$replay = DB::table('audit_events')->where('source_system', 'tempo-preparation-publication')
->where('after_data->operation_key', $data['operation_key'])->first();
if ($replay) {
$prior = json_decode($replay->after_data, true);
abort_unless($prior['record_id'] === $id && (int) $replay->actor_user_id === $actor->id && hash_equals($prior['request_hash'], $digest), 409, 'Chave usada noutra operação.');
return ['id' => $id, 'replayed' => true, 'message' => 'Operação já registada; nenhuma alteração repetida.'];
}
abort_unless(($preview['expires'] ?? 0) >= now()->timestamp, 409, 'A prévia expirou. Prepare outra prévia.');
abort_unless(hash_equals(app(OpportunityManagement::class)->etag($record), $preview['etag']), 409, 'Alteração concorrente: a ficha foi preservada. Reabra e reveja a nova versão.');
$before = $record->getRawOriginal();
$oldLocations = DB::table('opportunity_locations')->where('opportunity_id', $id)->pluck('geo_location_id')->all();
$context = $record->workflow_context ?? [];
if ($preview['action'] === 'withdraw') {
$record->status = 'suspended';
$record->published_at = null;
$context['publication']['visibility'] = 'withdrawn';
$action = 'promotional_summary_withdrawn';
} else {
$p = $preview['projection'];
if ($p['sector_id'] !== null) abort_unless(\App\Models\Sector::catalog()->whereKey($p['sector_id'])->exists(), 409, 'O sector deixou de estar disponível no catálogo. Prepare outra prévia.');
$paths = [];
foreach ($preview['input']['territory_ids'] ?? [] as $territory) {
$path = app(InvestmentGeography::class)->path($territory, true);
abort_unless(count($path) && collect($path)->contains('id', $territory), 409, 'Um território deixou de estar disponível. Prepare outra prévia.');
$paths[] = $path;
}
$currentTerritories = array_map(fn ($path) => ['id' => end($path)->id, 'label' => implode(' / ', array_column($path, 'name'))], $paths);
abort_unless($currentTerritories === $p['territories'], 409, 'O enquadramento territorial mudou. Prepare outra prévia.');
// Preserve the original private draft bytes; retain its former base
// columns in the existing hidden context before projecting public text.
$context['promotional_private_base'] ??= $record->only(OpportunityManagement::FIELDS);
$context['working_state'] ??= app(PortalProfileAccess::class)->state($record);
$context['promotional_summary'] = ['input' => $preview['input'], 'projection' => $p];
$context['publication'] = ['visibility' => 'public', 'purpose' => self::PURPOSE, 'actor_id' => $actor->id,
'at' => now()->toIso8601String(), 'execution_approved' => false, 'private_draft_preserved' => true];
$action = data_get($before, 'status') === 'published' ? 'promotional_summary_updated' : 'promotional_summary_published';
foreach (['title', 'summary', 'description', 'sector_id', 'maturity_label'] as $field) $record->{$field} = $p[$field];
foreach (['promoter_name', 'promoter_type', 'financing_modality', 'expected_impact', 'maturity_level', 'estimated_investment_amount', 'estimated_investment_currency'] as $field) $record->{$field} = null;
$main = $paths[0] ?? [];
$record->primary_territory_id = $main ? end($main)->id : null;
$record->province = collect($main)->firstWhere('location_type', 'province')?->name;
$record->district = collect($main)->firstWhere('location_type', 'district')?->name;
$record->geography_scope = count($paths) > 1 ? 'multiple' : ($main ? 'specific' : 'unspecified');
DB::table('opportunity_locations')->where('opportunity_id', $id)->delete();
foreach ($preview['input']['territory_ids'] ?? [] as $territory) DB::table('opportunity_locations')->insert(['opportunity_id' => $id, 'geo_location_id' => $territory]);
$record->status = 'published';
$record->is_featured = false;
$record->last_validated_at = null;
$record->published_at = now();
}
$record->workflow_context = $context;
$record->management_version++;
$record->save();
DB::table('audit_events')->insert(['source_system' => 'tempo-preparation-publication', 'entity_type' => 'opportunity',
'actor_user_id' => $actor->id, 'action' => $action, 'data_classification' => 'restricted', 'correlation_id' => (string) Str::uuid(),
'before_data' => json_encode(['record_id' => $id, 'record' => $before, 'territories' => $oldLocations], JSON_THROW_ON_ERROR),
'after_data' => json_encode(['record_id' => $id, 'operation_key' => $data['operation_key'], 'request_hash' => $digest,
'reason' => $data['reason'], 'projection' => $preview['projection'], 'execution_approved' => false,
'record' => $record->getRawOriginal()], JSON_THROW_ON_ERROR)]);
return ['id' => $id, 'replayed' => false, 'message' => $preview['action'] === 'withdraw' ? 'Resumo retirado; cadastro e histórico preservados.' : 'Resumo divulgado como Em preparação. Não autoriza execução do investimento.'];
});
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Services;
use App\Models\Opportunity;
/** Presentation only: originals, editorial fields and audit records remain intact. */
final class OpportunityPublicText
{
public static function description(Opportunity $record, string $text): string
{
$source = data_get($record->workflow_context, 'corridor_agribusiness');
if (! is_array($source) || ($source['source_sha256'] ?? null) !== CorridorAgribusinessOpportunities::SOURCE_HASH) return $text;
$hash = CorridorAgribusinessOpportunities::SOURCE_HASH;
$sourceLine = 'Fonte: '.$source['source'].'; SHA-256 '.$hash;
$paragraphs = explode("\n\n", $text);
$result = [];
foreach ($paragraphs as $paragraph) {
$paragraph = trim($paragraph);
if (in_array($paragraph, [$sourceLine, $sourceLine.'.', 'Edição: Não indicada.', 'Data-base: Não indicada.',
'Limitações e lacunas: '.CorridorAgribusinessOpportunities::LIMITATIONS,
OpportunityPromotion::NOTICE], true)) continue;
if ($paragraph === 'Enquadramento documental: '.$source['corridor'].'.') $paragraph = $source['corridor'].'.';
if ($paragraph === CorridorAgribusinessOpportunities::LIMITATIONS) {
$paragraph = 'A localização exacta, a disponibilidade de terreno e as condições do investimento serão definidas durante a preparação do projecto.';
}
if ($paragraph !== '') $result[] = $paragraph;
}
return implode("\n\n", $result);
}
}
+5 -2
View File
@@ -184,7 +184,9 @@ class PortalProfileAccess
public function canCreate(User $actor, ?string $organization, string $type): bool
{
$included = in_array($type, OpportunityCanonicalContract::BASE_CATEGORIES, true) || app(OpportunityModularProfiles::class)->enabled();
// Draft preparation is allowed within the assigned modality. Executable
// submission support is checked separately by the canonical contract.
$included = in_array($type, OpportunityManagement::CATEGORIES, true);
// This is creation as an authorized Promotor, never an exceptional SU act.
return $included && $this->available() && $this->scoped($actor, 'promoter', $organization, $type) && ! $this->has($actor, 'auditor');
@@ -370,7 +372,8 @@ class PortalProfileAccess
foreach (json_decode($grant->opportunity_types, true, 512, JSON_THROW_ON_ERROR) as $type) {
$key = ($grant->organization_id ?? 'individual').'|'.$type;
if (! isset($seen[$key]) && $this->canCreate($actor, $grant->organization_id, $type)) {
$contexts[] = ['organization_id' => $grant->organization_id, 'type' => $type];
$contexts[] = ['organization_id' => $grant->organization_id,
'organization_name' => $grant->organization_id ? DB::table('organizations')->where('id', $grant->organization_id)->value('name') : null, 'type' => $type];
$seen[$key] = true;
}
}
+287
View File
@@ -0,0 +1,287 @@
<?php
namespace App\Services;
use App\Models\Opportunity;
use App\Models\User;
use Carbon\CarbonImmutable;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
/** Publication of notices; never receives bids or changes the linked investment. */
final class Procurement
{
public const TEXT = ['title', 'reference', 'responsible_unit', 'modality', 'object', 'object_kind', 'quantities', 'financier', 'instrument', 'edition', 'rule_source', 'financing_agreement', 'eligibility', 'requirements', 'proposal_validity', 'guarantee_status', 'guarantees', 'delivery_channel', 'delivery_address', 'delivery_url', 'public_contact', 'dissemination', 'origin_url', 'origin_published_at', 'clarifications_at', 'deadline', 'opening_at', 'timezone', 'minimum_source', 'minimum_edition', 'minimum_modality', 'minimum_day_basis', 'result', 'internal_notes', 'public_reason', 'minimum_working_days', 'minimum_holidays', 'minimum_calendar_source'];
public function available(): bool
{
return config('procurement.enabled') && Schema::hasTable('portal_tenders');
}
public function access(?User $actor): bool
{
if (! $actor || ! $actor->hasVerifiedEmail()) {
return false;
}
$docs = app(DocumentManagementAccess::class);
return ! app(PortalProfileAccess::class)->has($actor, 'auditor') && ($docs->full($actor) || $docs->editorial($actor));
}
public function guard(?User $actor): void
{
abort_unless($this->available(), 404);
abort_unless($this->access($actor), 403, 'Use a autorização editorial já atribuída.');
}
public function references(User $actor): array
{
$this->guard($actor);
$editable = app(DocumentManagementAccess::class)->documents($actor)->select('id');
return ['regimes' => config('procurement.regimes'), 'acts' => config('procurement.acts'),
'organizations' => DB::table('organizations')->whereNull('deleted_at')->orderBy('name')->get(['id', 'name']),
'sectors' => DB::table('sectors')->orderBy('name')->get(['id', 'name']),
'territories' => app(InvestmentGeography::class)->references(true)->orderBy('name')->get(['id', 'name', 'location_type']),
'opportunities' => app(PortalProfileAccess::class)->scope(Opportunity::query(), $actor)->orderBy('title')->get(['id', 'title']),
'projects' => DB::table('projects')->whereNull('deleted_at')->whereIn('data_classification', ['internal', 'public'])->orderBy('name')->get(['id', 'name']),
'documents' => DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')->whereNull('d.deleted_at')
->where(fn ($q) => $q->whereIn('d.id', $editable)->orWhere(fn ($p) => $p->where('r.published', true)->where('d.data_classification', 'public')))
->orderBy('r.title')->get(['r.id', 'r.title', 'r.published', 'd.malware_scan_status'])];
}
public function record(string $id): object
{
abort_unless($this->available(), 404);
return DB::table('portal_tenders')->where('id', $id)->firstOrFail();
}
public function save(User $actor, ?string $id, array $input): string
{
$this->guard($actor);
$v = Validator::make($input, ['version' => 'required|integer|min:0', 'reason' => 'required|string|min:20|max:1000', 'draft' => 'required|array']);
$v->validate();
$draft = $this->normalize($actor, $input['draft']);
return DB::transaction(function () use ($actor, $id, $input, $draft) {
$old = $id ? DB::table('portal_tenders')->where('id', $id)->lockForUpdate()->firstOrFail() : null;
abort_unless((int) ($old->version ?? 0) === (int) $input['version'], 409, 'A versão mudou; recupere o concurso antes de guardar.');
$id ??= (string) Str::uuid();
$version = ($old->version ?? 0) + 1;
if ($old) {
DB::table('portal_tenders')->where('id', $id)->update(['draft' => json_encode($draft, JSON_THROW_ON_ERROR), 'version' => $version, 'updated_at' => now()]);
} else {
DB::table('portal_tenders')->insert(['id' => $id, 'draft' => json_encode($draft, JSON_THROW_ON_ERROR), 'version' => $version, 'created_by' => $actor->id, 'created_at' => now(), 'updated_at' => now()]);
}
$this->version($actor, $id, $version, 'draft', $draft, $input['reason'], false);
return $id;
});
}
private function normalize(User $actor, array $input): array
{
$rules = array_fill_keys(self::TEXT, 'nullable|string|max:12000');
$rules = [...$rules, 'regime' => 'nullable|in:state,external,private,other', 'organization_id' => 'nullable|uuid|exists:organizations,id',
'opportunity_id' => 'nullable|integer|exists:opportunities,id', 'project_id' => 'nullable|uuid|exists:projects,id', 'sector_id' => 'nullable|integer|exists:sectors,id',
'territory_id' => 'nullable|uuid|exists:geo_locations,id', 'contact_authorized' => 'boolean', 'minimum_days' => 'nullable|integer|min:0|max:3650',
'lots' => 'array|max:100', 'lots.*' => 'array:code,description,quantities', 'lots.*.code' => 'required|string|max:80|distinct', 'lots.*.description' => 'required|string|max:3000', 'lots.*.quantities' => 'nullable|string|max:1500',
'attachments' => 'array|max:100', 'attachments.*' => 'array:resource_id,public,purpose', 'attachments.*.resource_id' => 'required|integer|exists:portal_resources,id', 'attachments.*.public' => 'required|boolean', 'attachments.*.purpose' => 'required|string|max:150'];
foreach (['origin_url', 'delivery_url', 'rule_source'] as $url) {
$rules[$url] = 'nullable|url:http,https|max:2000';
}
$data = Validator::make($input, $rules)->validate();
if (! empty($data['opportunity_id'])) {
abort_unless(app(PortalProfileAccess::class)->scope(Opportunity::query(), $actor)->whereKey($data['opportunity_id'])->exists(), 403);
}
if (! empty($data['project_id'])) {
abort_unless(DB::table('projects')->where('id', $data['project_id'])->whereNull('deleted_at')->whereIn('data_classification', ['internal', 'public'])->exists(), 422);
}
foreach ($data['attachments'] ?? [] as $attachment) {
$resource = DB::table('portal_resources')->where('id', $attachment['resource_id'])->firstOrFail();
abort_unless($this->publicDocuments()->where('r.id', $resource->id)->exists() || app(DocumentManagementAccess::class)->documents($actor)->where('id', $resource->document_id)->exists(), 403);
}
return $data;
}
public function preview(User $actor, string $id, string $act): array
{
$this->guard($actor);
$record = $this->record($id);
abort_unless(array_key_exists($act, config('procurement.acts')), 422);
$p = json_decode($record->draft, true, 512, JSON_THROW_ON_ERROR);
Validator::make($p, array_fill_keys(['title', 'reference', 'organization_id', 'modality', 'object', 'regime', 'instrument', 'edition', 'rule_source', 'origin_published_at', 'deadline', 'delivery_channel', 'timezone'], 'required'))->validate();
Validator::make($p, ['timezone' => 'timezone', 'object_kind' => 'nullable|in:works,goods,services,mixed,other', 'guarantee_status' => 'nullable|in:required,not_required,unknown'])->validate();
Validator::make($p, ['public_reason' => 'required|string|min:20|max:1000'])->validate();
$dates = [];
foreach (['origin_published_at', 'clarifications_at', 'deadline', 'opening_at'] as $field) {
if (empty($p[$field])) {
continue;
}
$validator = Validator::make([$field => $p[$field]], [$field => 'date_format:Y-m-d\TH:i']);
$validator->validate();
$dates[$field] = CarbonImmutable::createFromFormat('!Y-m-d\TH:i', $p[$field], $p['timezone']);
}
$this->require($dates['deadline']->greaterThan($dates['origin_published_at']), 'deadline', 'Entrega anterior à publicação de origem.');
if (isset($dates['clarifications_at'])) {
$this->require($dates['clarifications_at']->betweenIncluded($dates['origin_published_at'], $dates['deadline']), 'clarifications_at', 'Esclarecimentos fora do período de publicação/entrega.');
}
if (isset($dates['opening_at'])) {
$this->require($dates['opening_at']->greaterThanOrEqualTo($dates['deadline']), 'opening_at', 'A abertura indicada é anterior à entrega.');
}
if (($p['guarantee_status'] ?? '') === 'required') {
$this->require(! empty($p['guarantees']), 'guarantees', 'Descreva a garantia documentada exigida.');
}
if (isset($p['minimum_days'])) {
foreach (['minimum_source', 'minimum_edition', 'minimum_modality', 'minimum_day_basis'] as $field) {
$this->require(! empty($p[$field]), $field, 'Identifique fonte, edição, modalidade e contagem do prazo mínimo.');
}
$this->require($p['minimum_modality'] === $p['modality'], 'minimum_modality', 'A regra indicada pertence a outra modalidade.');
$earliest = $dates['origin_published_at'];
if ($p['minimum_day_basis'] === 'business') {
$this->require(! empty($p['minimum_calendar_source']), 'minimum_calendar_source', 'Identifique a fonte do calendário de dias úteis e feriados.');
$weekdays = array_filter(array_map('trim', explode(',', (string) ($p['minimum_working_days'] ?? ''))));
$this->require(count($weekdays) > 0 && ! array_diff($weekdays, ['1', '2', '3', '4', '5', '6', '7']), 'minimum_working_days', 'Indique os dias de trabalho documentados (1=segunda, 7=domingo).');
$holidays = array_values(array_filter(preg_split('/[\s,;]+/', (string) ($p['minimum_holidays'] ?? ''))));
Validator::make(['dates' => $holidays], ['dates' => 'array|max:1000', 'dates.*' => 'date_format:Y-m-d'])->validate();
for ($left = (int) $p['minimum_days'],$iterations = 0; $left > 0; $iterations++) {
$this->require($iterations < 36500, 'minimum_days', 'Calendário insuficiente para calcular o prazo indicado.');
$earliest = $earliest->addDay();
if (in_array((string) $earliest->isoWeekday(), $weekdays, true) && ! in_array($earliest->format('Y-m-d'), $holidays, true)) {
$left--;
}
}
} else {
$this->require($p['minimum_day_basis'] === 'calendar', 'minimum_day_basis', 'Identifique dias corridos ou calendário de dias úteis documentado.');
$earliest = $earliest->addDays((int) $p['minimum_days']);
}
$this->require($dates['deadline']->greaterThanOrEqualTo($earliest), 'deadline', 'Prazo inferior ao mínimo documentado para esta modalidade/edição/calendário.');
}
$previous = $record->published_version_id ? $this->publishedVersion($record->published_version_id) : null;
$this->require($previous ? $act !== 'initial' : $act === 'initial', 'act', $previous ? 'O anúncio inicial já existe; publique um acto associado.' : 'Publique primeiro o anúncio inicial.');
if ($previous) {
$before = $previous['payload'];
if (($before['deadline'] ?? '') !== $p['deadline'] || ($before['timezone'] ?? '') !== $p['timezone']) {
$this->require($act === 'extension', 'act', 'Uma mudança do prazo exige acto de prorrogação, com data anterior e nova.');
}
if ($act === 'extension') {
$this->require($dates['deadline']->greaterThan(CarbonImmutable::parse($before['deadline'], $before['timezone'])), 'deadline', 'A prorrogação deve avançar o prazo anterior.');
$p['deadline_previous'] = $before['deadline'];
$p['timezone_previous'] = $before['timezone'];
}
$p['extended'] = ($before['extended'] ?? false) || $act === 'extension';
$p['procedural_state'] = $before['procedural_state'] ?? null;
}
if ($act === 'award') {
$p['procedural_state'] = 'results';
}
if ($act === 'cancellation') {
$p['procedural_state'] = 'cancelled';
}
if (in_array($act, ['award', 'cancellation', 'other'], true)) {
$this->require(! empty($p['result']), 'result', 'Descreva o acto ou resultado documentado.');
}
$p['organization_name'] = DB::table('organizations')->where('id', $p['organization_id'])->whereNull('deleted_at')->value('name');
$this->require((bool) $p['organization_name'], 'organization_id', 'Entidade eliminada; recupere a referência.');
$p['sector_name'] = empty($p['sector_id']) ? null : DB::table('sectors')->where('id', $p['sector_id'])->value('name');
$p['territory_name'] = empty($p['territory_id']) ? null : app(InvestmentGeography::class)->references(true)->where('id', $p['territory_id'])->value('name');
if (! empty($p['territory_id'])) {
$this->require((bool) $p['territory_name'], 'territory_id', 'Referência territorial indisponível.');
}
unset($p['internal_notes']);
if (! ($p['contact_authorized'] ?? false)) {
unset($p['public_contact']);
}
$attachments = [];
foreach ($p['attachments'] ?? [] as $item) {
if (! $item['public']) {
continue;
}
$doc = $this->publicDocuments()->where('r.id', $item['resource_id'])->first(['r.id', 'r.title', 'd.sha256', 'd.id as document_id']);
$this->require((bool) $doc, 'attachments', 'Anexo público exige publicação autorizada, scan limpo e direitos sem restrição na biblioteca.');
$attachments[] = [...$item, 'title' => $doc->title, 'sha256' => trim($doc->sha256), 'document_id' => $doc->document_id];
}
$p['attachments'] = $attachments;
if (! empty($p['opportunity_id'])) {
$o = Opportunity::published()->whereKey($p['opportunity_id'])->first(['id', 'title', 'slug']);
$p['opportunity'] = $o ? ['title' => $o->title, 'url' => '/oportunidades/'.$o->slug] : null;
}
if (! empty($p['project_id'])) {
$p['project_name'] = DB::table('projects')->where('id', $p['project_id'])->whereNull('deleted_at')->where('data_classification', 'public')->value('name');
}
unset($p['opportunity_id'],$p['project_id']);
$plan = ['id' => $id, 'version' => $record->version, 'act' => $act, 'payload' => $p];
$plan['hash'] = hash_hmac('sha256', json_encode($plan, JSON_THROW_ON_ERROR), config('app.key'));
return $plan;
}
public function publish(User $actor, string $id, array $input): void
{
$this->guard($actor);
Validator::make($input, ['hash' => 'required|string|size:64', 'act' => 'required|string', 'reason' => 'required|string|min:20|max:1000'])->validate();
DB::transaction(function () use ($actor, $id, $input) {
$record = DB::table('portal_tenders')->where('id', $id)->lockForUpdate()->firstOrFail();
$plan = $this->preview($actor, $id, $input['act']);
abort_unless(hash_equals($plan['hash'], $input['hash']), 409, 'Prévia alterada; reveja o conteúdo antes de publicar.');
abort_unless($input['reason'] === $plan['payload']['public_reason'], 409, 'O motivo público deve coincidir com a prévia.');
$version = $record->version + 1;
$vid = $this->version($actor, $id, $version, $input['act'], $plan['payload'], $input['reason'], true);
DB::table('portal_tenders')->where('id', $id)->update(['published_version_id' => $vid, 'version' => $version, 'updated_at' => now()]);
});
}
private function version(User $actor, string $id, int $version, string $act, array $payload, string $reason, bool $published): string
{
$vid = (string) Str::uuid();
DB::table('portal_tender_versions')->insert(['id' => $vid, 'tender_id' => $id, 'version' => $version, 'act' => $act, 'payload' => json_encode($payload, JSON_THROW_ON_ERROR), 'reason' => $reason, 'actor_id' => $actor->id, 'published_at' => $published ? now() : null, 'created_at' => now()]);
DB::table('audit_events')->insert(['source_system' => 'procurement', 'entity_type' => 'portal_tenders', 'entity_id' => $id, 'actor_user_id' => $actor->id, 'action' => $published ? 'notice_published' : 'tender_draft_saved', 'data_classification' => 'internal', 'after_data' => json_encode(['version_id' => $vid, 'act' => $act, 'reason' => $reason], JSON_THROW_ON_ERROR)]);
return $vid;
}
public function publicDocuments(): Builder
{
return DB::table('portal_resources as r')->join('documents as d', 'd.id', '=', 'r.document_id')->where('r.published', true)->where('r.rights_status', '!=', 'restricted')->where('d.rights_status', '!=', 'restricted')->where('d.data_classification', 'public')->where('d.status', 'active')->whereNull('d.deleted_at')->where('d.malware_scan_status', 'clean')->whereColumn('d.sha256', 'd.malware_scanned_sha256')
->where(fn ($q) => $q->whereNull('d.source_system')->orWhereNotIn('d.source_system', ['opportunity-resources', 'opportunity-image-derivatives']))
->whereNotExists(fn ($q) => $q->selectRaw('1')->from('opportunity_documents as od')->whereColumn('od.document_id', 'd.id')->whereRaw("NOT jsonb_exists(COALESCE(od.payload,'{}'::jsonb),'library_source')"))
->whereNotExists(fn ($q) => $q->selectRaw('1')->from('opportunity_images as oi')->whereColumn('oi.document_id', 'd.id')->whereRaw("NOT jsonb_exists(COALESCE(oi.payload,'{}'::jsonb),'library_source')"));
}
public function publishedVersion(string $id): array
{
$v = DB::table('portal_tender_versions')->where('id', $id)->whereNotNull('published_at')->firstOrFail();
$p = json_decode($v->payload, true, 512, JSON_THROW_ON_ERROR);
foreach ($p['attachments'] ?? [] as $i => $item) {
$p['attachments'][$i]['available'] = $this->publicDocuments()->where('r.id', $item['resource_id'])->where('d.id', $item['document_id'])->where('d.sha256', $item['sha256'])->exists();
}
return ['id' => $v->id, 'version' => $v->version, 'act' => $v->act, 'payload' => $p, 'published_at' => $v->published_at, 'reason' => $v->reason];
}
public function state(array $version): string
{
$p = $version['payload'];
if (in_array($p['procedural_state'] ?? '', ['cancelled', 'results'], true)) {
return $p['procedural_state'];
}
if (CarbonImmutable::parse($p['deadline'], $p['timezone'])->isPast()) {
return 'deadline_ended';
}
return ($p['extended'] ?? false) ? 'extended' : 'open';
}
private function require(bool $condition, string $field, string $message): void
{
if (! $condition) {
throw ValidationException::withMessages([$field => $message]);
}
}
}
+37 -1
View File
@@ -60,9 +60,11 @@ final class SimulationFormDrafts
throw new HttpException(409, 'Já existe um rascunho activo. Carregue-o antes de guardar novas alterações.');
}
if ($case) $data['economic_snapshot'] = $this->landContext($data['economic_snapshot'], $case);
$snapshotHash = hash('sha256', json_encode($this->canonical($data), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
$draft = SimulationFormDraft::query()->create([
'id' => (string) Str::uuid(), 'owner_user_id' => $actorId, 'anonymous_token_hash' => $tokenHash,
'version_no' => 1, 'create_key_hash' => $keyHash, 'create_payload_hash' => $payloadHash, 'snapshot_sha256' => $payloadHash,
'version_no' => 1, 'create_key_hash' => $keyHash, 'create_payload_hash' => $payloadHash, 'snapshot_sha256' => $snapshotHash,
'personal_snapshot' => SimulationFormDraft::encryptSnapshot($data['personal_snapshot']),
'economic_snapshot' => SimulationFormDraft::encryptSnapshot($data['economic_snapshot']),
'expires_at' => $actorId === null ? now()->addDays(30) : null,
@@ -89,6 +91,7 @@ final class SimulationFormDrafts
$this->checkContext($request, $draft);
$this->requireMatch($request, $draft);
$previousEconomic = SimulationFormDraft::decryptSnapshot($draft->economic_snapshot);
$data['economic_snapshot'] = $this->preserveInactiveLandStatus($draft, $data['economic_snapshot'], $previousEconomic, $actorId);
if (($data['economic_snapshot']['reuse_simulation'] ?? null) === 'no') {
unset($data['economic_snapshot']['simulation_sources']);
} elseif (is_array($previousEconomic['simulation_sources'] ?? null)) {
@@ -456,6 +459,39 @@ final class SimulationFormDrafts
return $query;
}
private function preserveInactiveLandStatus(SimulationFormDraft $draft, array $economic, array $previous, ?int $actorId): array
{
if (! $actorId || ! config('investor-path.enabled') || ! app(VirtualDesk::class)->installed()) return $economic;
// Use the real owner-bound dossier even if a request omits its context parameter.
$case = DB::table('desk_cases')->where('simulation_id', $draft->id)->where('user_id', $actorId)->first();
if (! $case) return $economic;
return $this->landContext($economic, $case, $previous);
}
private function landContext(array $economic, \stdClass $case, ?array $previous = null): array
{
$snapshot = json_decode($economic['context_applicability_snapshot'] ?? $previous['context_applicability_snapshot'] ?? '', true);
$key = $economic['context_opportunity'] ?? $snapshot['consumer_opportunity'] ?? null;
$key = app(InvestmentConsumerScope::class)->key(app(InvestorContext::class)->state($case), is_string($key) ? $key : null);
$decision = app(InvestorContext::class)->landPreparation($case, $key);
if ($decision['need'] !== 'yes' && $previous !== null) {
if (array_key_exists('land_status', $previous)) $economic['land_status'] = $previous['land_status'];
else unset($economic['land_status']);
if (isset($previous['selection_labels']['land_status'])) $economic['selection_labels']['land_status'] = $previous['selection_labels']['land_status'];
else unset($economic['selection_labels']['land_status']);
}
$snapshot = json_decode($economic['context_applicability_snapshot'] ?? $previous['context_applicability_snapshot'] ?? '', true);
if (! is_array($snapshot) || ($snapshot['version'] ?? null) !== 1) {
$snapshot = ['version' => 1, 'context_revision' => app(InvestorContext::class)->state($case)['revision']];
}
$snapshot['land_preparation'] = $decision;
$snapshot['consumer_opportunity'] = $key;
if ($key) $economic['context_opportunity'] = $key;
$economic['context_applicability_snapshot'] = json_encode($snapshot, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
return $economic;
}
private function checkContext(Request $request, SimulationFormDraft $draft): void
{
$context = $request->input('context');
+1 -1
View File
@@ -20,7 +20,7 @@ class TerritoryGeometryImport
private function guard(User $actor, bool $write = false): void
{
abort_unless($actor->can('manage-portal'), 403);
abort_unless($actor->can('manage-portal') || app(ScopedPortalAdministration::class)->administrative($actor), 403);
abort_unless(config('geometry-import.enabled'), 503, 'Importação cartográfica não disponibilizada.');
if (! $write) {
return;
+18 -4
View File
@@ -170,11 +170,11 @@ class VirtualDesk
});
}
public function createRequest(User $actor, string $caseId, string $serviceId, string $clientKey): \stdClass
public function createRequest(User $actor, string $caseId, string $serviceId, string $clientKey, ?string $opportunity = null): \stdClass
{
abort_unless(Str::isUuid($serviceId) && Str::isUuid($clientKey), 422);
return DB::transaction(function () use ($actor, $caseId, $serviceId, $clientKey) {
return DB::transaction(function () use ($actor, $caseId, $serviceId, $clientKey, $opportunity) {
$case = $this->case($actor, $caseId, true, true);
abort_if($case->closed_at, 409, 'Dossier encerrado.');
$service = DB::table('desk_service_versions')->where('id', $serviceId)->where('status', 'published')->first();
@@ -197,7 +197,10 @@ class VirtualDesk
$this->event($case, $actor, 'request_prefill_prepared', ['draft_id' => $draft->id, 'draft_version' => $draft->version_no, 'snapshot_sha256' => $draft->snapshot_sha256, 'fields' => ['title', 'description']]);
}
$context = app(InvestorContext::class)->state($case);
$mapped = app(InvestorContext::class)->prefill($context)['desk'];
if (count($context['opportunity_choices'] ?? []) > 1 && !$opportunity) $payload['description'] = '';
$mapped = app(InvestorContext::class)->prefill($context, $opportunity)['desk'];
if ($opportunity) $payload['context_opportunity'] = $opportunity;
$payload = app(InvestmentConsumerScope::class)->bind($actor, $caseId, $payload);
$serviceFields = json_decode($service->definition, true)['fields'];
foreach (['territory_id' => 'territory_id', 'location_source' => 'location_source', 'title' => 'title', 'description' => 'description', 'sector' => 'sector', 'province_id' => 'province_id', 'district_id' => 'district_id', 'admin_post_id' => 'admin_post_id', 'locality_id' => 'locality_id'] as $field => $source) {
if (isset($serviceFields[$field],$mapped[$source]) && (empty($payload[$field]) || in_array($field, ['title', 'description'], true) && $context['revision'] > 0)) {
@@ -230,6 +233,12 @@ class VirtualDesk
$draft = SimulationFormDraft::query()->whereKey($draft->id)->lockForUpdate()->firstOrFail();
}
$economic = SimulationFormDraft::decryptSnapshot($draft->economic_snapshot);
$contextChoices = app(InvestorContext::class)->state($case)['opportunity_choices'] ?? [];
if (count($contextChoices) > 1) {
$snapshot = json_decode($economic['context_applicability_snapshot'] ?? '{}', true);
$draftChoices = $snapshot['opportunity_choices'] ?? [];
if (count($draftChoices) !== 1 || ($draftChoices[0]['opportunity']['key'] ?? null) !== ($payload['context_opportunity'] ?? null)) return null;
}
$description = (string) ($economic['project_summary'] ?? '');
if (trim($description) === '') {
return null;
@@ -250,7 +259,9 @@ class VirtualDesk
* @return array<string,mixed> */
public function validateFields(array $payload, array $definition, bool $submitting): array
{
$rules = ['payload' => ['required', 'array:'.implode(',', [...array_keys($definition['fields']), 'country', 'declared_origin_country_code', 'context_reuse'])],
$rules = ['payload' => ['required', 'array:'.implode(',', [...array_keys($definition['fields']), 'country', 'declared_origin_country_code', 'context_reuse', 'context_opportunity', 'context_opportunity_snapshot'])],
'payload.context_opportunity' => ['nullable', 'string', 'max:100'],
'payload.context_opportunity_snapshot' => ['nullable', 'string', 'max:200000'],
'payload.context_reuse' => ['nullable', 'string', 'max:10000'],
'payload.country' => ['nullable', 'string', 'max:100', app(CountryCatalogue::class)->legacyCountryRule()],
'payload.declared_origin_country_code' => ['nullable', 'string', app(CountryCatalogue::class)->rule()]];
@@ -334,6 +345,9 @@ class VirtualDesk
}
}
$payload = $this->validateFields($data['payload'], $this->definition($request), $submit);
if ($request->status !== 'returned' || in_array('context_opportunity', json_decode($request->editable_fields ?? '[]', true), true)) {
$payload = app(InvestmentConsumerScope::class)->bind($actor, $request->case_id, $payload, $submit);
}
if ($request->status === 'returned') {
$editable = json_decode($request->editable_fields ?? '[]', true);
$previous = $this->decode($request->payload);
+13
View File
@@ -0,0 +1,13 @@
<?php
// Functional configuration expressly defined by Tempo, 23/10/2026.
// Codes identify the existing national territorial registry; no geometry is inferred.
return [
'pemba-lichinga' => ['name' => 'Corredor de Pemba–Lichinga', 'provinces' => ['02', '01']],
'nacala' => ['name' => 'Corredor de Nacala', 'provinces' => ['03']],
'vale-zambeze' => ['name' => 'Corredor do Vale do Zambeze', 'provinces' => ['05', '04']],
'beira' => ['name' => 'Corredor da Beira', 'provinces' => ['07', '06']],
'limpopo' => ['name' => 'Corredor do Limpopo', 'provinces' => ['09']],
// Tempo's final clarification: both Maputo Province and Maputo City.
'maputo' => ['name' => 'Corredor de Maputo', 'provinces' => ['10', '11']],
];
+170
View File
@@ -0,0 +1,170 @@
[
{
"external_id": "legacy-tempo-post-62413-20170626",
"sha256": "b471c1afd7235f359acdcc16a93088f77e1f988706c8d3c4d4f11c5d560aea47",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-73945-20170731",
"sha256": "2aa0ce0eb305bfdf93e390a978d3694d0fbc0129034077b68cebce418f878415",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-76474-20170808",
"sha256": "8f769aec4715449d056c8c25294060b2ff290ec0893c28e9bdcc4e8092fc75f5",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-62414-20170626",
"sha256": "a040c0b7cb95dd7f053f4d13662dd58b2b06453b6fa61a2d531d0257a8db8c17",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-62418-20170626",
"sha256": "e8ccafa68f0d1aa80f2e41d9e09f7b13e7c653a8472a731798cfd5e04ce2963e",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-75002-20170804",
"sha256": "7b6fbd998be2b47b96e9a4724560c192c502709fa308d6590c0797ddcdb10544",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-73246-20170728",
"sha256": "430ee8674c71104fc126bed2911d676f150ac623857a1ada75662987fd9ac390",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-77232-20170811",
"sha256": "27544d3127803b7495bef9fd633147d0225d8c31816c4d59ed05a2edd59de070",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-62416-20170626",
"sha256": "ac575ce340571192552c89b63f0bd62a84dbc47828e6930133af5ff70e249319",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-62929-20170627",
"sha256": "488017b062c6760659d9147b7f12aea4afd8d824121adb9462e2c7a2ae58437b",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-62649-20170626",
"sha256": "38a858713472759a2d20a308bdd9d8afa9cfb76dcaf8d11ca709e89261573e42",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-62739-20170626",
"sha256": "5870fc0fad9bb7af537b7b1468151cde138b1b886ce846c7be25647b630245dd",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-62854-20170627",
"sha256": "44b1ba9d593bb625cf53de8909e78eb0b3129742c3ec9d58ec68c984e8930bce",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-63128-20170627",
"sha256": "74be8f29e5053f53ab1a39a2f63208145c30f23357ca6d78b637dd02bf9c1fb4",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-63181-20170627",
"sha256": "3d0f9ac14d36156d8178e33f8616d6406f31ea4cd69c8308cbf4227586d7ccb3",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-63146-20170627",
"sha256": "542353466cc6d82b651619c495f138b0576a4be41d21aad194ae2005050cb1bc",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-62688-20170626",
"sha256": "a210a789b06d425822f4124f74226a2b27246c086c5ec5f041f1b2237d7c2b65",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-63248-20170628",
"sha256": "6c30e61197b8568332af6d9b19bbba7ad8665b3873bb031a8c8aedd41536b060",
"kind": "historico_noticioso",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173220-20260616",
"sha256": "5b76c3d84dcf7f9a489686d90e3857ae6536705c918e841310bb42b354e80976",
"kind": "setorial",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173222-20260616",
"sha256": "e64d7063d1ac4dda028c97e1ba64938d2a9e426a0b630c57ee5df52a41312cc7",
"kind": "setorial",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173482-20260915",
"sha256": "6f888e133a779c719073246f89bcae150198e5897c0e7d9b7ba75f54c54a7830",
"kind": "trabalho_domestico",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173484-20260915",
"sha256": "297df455a9c6fd1f0ab7ca7bbe124ce483c86fe55be26baa695f03089b3ac049",
"kind": "trabalho_domestico",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173478-20260915",
"sha256": "d4e320ed99dd9d53d205e47932f73651976d43081d5d39f2bfc2996901408644",
"kind": "trabalho_domestico",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173480-20260915",
"sha256": "c3ee6401eda5b582c61969b5e3b74fdf26ef34ca481b1aca8c4c309d845147ae",
"kind": "trabalho_domestico",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173476-20260915",
"sha256": "72fbf28391db4804e0637b64e7f59e9fa597812a2681b4562dc3d6dfb4093a30",
"kind": "trabalho_domestico",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173470-20260915",
"sha256": "26feeae6b8d04972c7a64ee360583cccdcaeb4b0d8795fd7bc2fd3c38866cc19",
"kind": "trabalho_domestico",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173474-20260915",
"sha256": "9fa741a17b746bfa2f3ce7b6ce1d2bb34d3a35d3b58330b0554f9e0a6844f049",
"kind": "trabalho_domestico",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
},
{
"external_id": "legacy-tempo-post-173463-20260915",
"sha256": "4f34b7bac4b3030c0614844d379f6e3cfad1b7f6ea3db575a58c33ae033b0e73",
"kind": "trabalho_domestico",
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
}
]
+1 -1
View File
@@ -23,7 +23,7 @@ return [
'opportunity_other_qa_subtypes' => [],
'operator_name' => env('PORTAL_OPERATOR_NAME'),
// Tempo, 19/10/2026: institutional contact; commercial department remains separate.
'institutional_contact_email' => env('PORTAL_INSTITUTIONAL_CONTACT_EMAIL', 'info@tempo.co.mz'),
'institutional_contact_email' => env('PORTAL_INSTITUTIONAL_CONTACT_EMAIL', 'comercial@tempo.co.mz'),
'support' => [
'email' => env('PORTAL_SUPPORT_EMAIL'),
'phone' => env('PORTAL_SUPPORT_PHONE'),
+7
View File
@@ -0,0 +1,7 @@
<?php
return [
'enabled' => (bool) env('PROCUREMENT_ENABLED', false),
'regimes' => ['state' => 'Contratação pública do Estado', 'external' => 'Financiamento externo', 'private' => 'Concurso privado', 'other' => 'Outro regime documentado'],
'acts' => ['initial' => 'Anúncio inicial', 'clarification' => 'Esclarecimento', 'correction' => 'Rectificação', 'extension' => 'Prorrogação', 'award' => 'Resultado / adjudicação', 'cancellation' => 'Cancelamento', 'other' => 'Outro acto'],
];
@@ -0,0 +1,181 @@
{
"source": "investimentos nos corredores.docx",
"source_sha256": "1473b06c48aa234e771660e3f2a51b9d2e4e1943bd52d878e0ae854ee6657055",
"introduction": "Os corredores de desenvolvimento integram cadeias de valor prioritárias que podem ser apresentadas no portal InvesteMoz como oportunidades concretas de investimento na produção, transformação, logística e comercialização. Para cada corredor, são destacadas abaixo as oportunidades associadas aos produtos com maior potencial.",
"segments": [
"Produção primária e fornecimento de insumos",
"Processamento e transformação agro-industrial",
"Armazenagem, cadeia de frio e logística",
"Embalagem, distribuição, comercialização e exportação",
"Infraestruturas e serviços de apoio às cadeias de valor"
],
"groups": [
{
"name": "Corredor de Pemba–Lichinga",
"opportunities": [
{
"chain": "Batata, trigo, feijão, milho e soja",
"description": "Produção comercial, armazenamento e processamento de batata, trigo, feijão, milho e soja."
},
{
"chain": "Algodão",
"description": "Descaroçamento, fiação e produção de óleo e derivados de algodão."
},
{
"chain": "Sésamo",
"description": "Limpeza, prensagem, embalagem e exportação de sésamo."
},
{
"chain": "Florestas",
"description": "Viveiros, plantações comerciais, madeira processada e produtos florestais sustentáveis."
},
{
"chain": "Avicultura",
"description": "Produção de rações, incubação, criação, abate e cadeia de frio para avicultura."
}
]
},
{
"name": "Corredor de Nacala",
"opportunities": [
{
"chain": "Mandioca",
"description": "Produção e transformação de mandioca em farinha, amido e outros derivados."
},
{
"chain": "Milho, amendoim e sésamo",
"description": "Produção, armazenagem e processamento de milho, amendoim e sésamo."
},
{
"chain": "Algodão",
"description": "Descaroçamento e transformação de algodão."
},
{
"chain": "Frutas e caju",
"description": "Pomares, processamento, embalagem e exportação de frutas e caju."
},
{
"chain": "Florestas",
"description": "Silvicultura comercial, madeira processada e produtos florestais."
},
{
"chain": "Avicultura",
"description": "Produção de rações, criação, abate e distribuição de produtos avícolas."
}
]
},
{
"name": "Corredor do Vale do Zambeze",
"opportunities": [
{
"chain": "Arroz",
"description": "Produção irrigada, moagem, embalagem e comercialização de arroz."
},
{
"chain": "Milho e batata",
"description": "Produção, secagem, armazenagem e transformação de milho e batata."
},
{
"chain": "Sésamo",
"description": "Limpeza, prensagem e exportação de sésamo."
},
{
"chain": "Algodão",
"description": "Descaroçamento e transformação de algodão."
},
{
"chain": "Avicultura",
"description": "Produção de rações, incubação, criação e abate de aves."
},
{
"chain": "Bovinos e caprinos",
"description": "Criação, engorda, abate, processamento de carne e cadeia de frio para bovinos e caprinos."
}
]
},
{
"name": "Corredor da Beira",
"opportunities": [
{
"chain": "Milho, trigo e arroz",
"description": "Produção, silos, moagem e processamento de milho, trigo e arroz."
},
{
"chain": "Soja",
"description": "Esmagamento de soja, produção de óleo vegetal e fabrico de rações."
},
{
"chain": "Hortícolas",
"description": "Produção irrigada, conservação, embalagem e distribuição de hortícolas."
},
{
"chain": "Sésamo",
"description": "Limpeza, prensagem e exportação de sésamo."
},
{
"chain": "Açúcar",
"description": "Produção e refinação de açúcar e aproveitamento de subprodutos."
},
{
"chain": "Florestas",
"description": "Plantações comerciais, processamento de madeira e produtos florestais."
},
{
"chain": "Carne bovina",
"description": "Criação, engorda, abate, processamento e cadeia de frio para carne bovina."
}
]
},
{
"name": "Corredor do Limpopo",
"opportunities": [
{
"chain": "Arroz",
"description": "Produção irrigada, moagem, embalagem e comercialização de arroz."
},
{
"chain": "Hortícolas",
"description": "Produção em estufas e campo aberto, conservação, embalagem e distribuição de hortícolas."
},
{
"chain": "Carne vermelha",
"description": "Criação, engorda, abate, processamento e cadeia de frio para carne vermelha."
},
{
"chain": "Avicultura",
"description": "Produção de rações, incubação, criação, abate e distribuição de produtos avícolas."
},
{
"chain": "Infraestruturas e serviços de apoio",
"description": "Infraestruturas de irrigação, armazenagem, transporte refrigerado e centros de comercialização."
}
]
},
{
"name": "Corredor de Maputo",
"opportunities": [
{
"chain": "Arroz",
"description": "Produção irrigada, moagem, embalagem e fornecimento de arroz aos mercados urbanos e regionais."
},
{
"chain": "Hortícolas",
"description": "Produção, conservação, embalagem, transformação e distribuição de hortícolas."
},
{
"chain": "Carne vermelha",
"description": "Criação, engorda, abate, processamento, distribuição e exportação regional de carne vermelha."
},
{
"chain": "Avicultura",
"description": "Produção de rações, incubação, criação, abate e comercialização de produtos avícolas."
},
{
"chain": "Infraestruturas e serviços de apoio",
"description": "Plataformas logísticas, centros de distribuição, cadeia de frio e serviços de exportação."
}
]
}
],
"sector": "Agronegócio"
}
@@ -0,0 +1,50 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('portal_tenders', function (Blueprint $t) {
$t->uuid('id')->primary();
$t->jsonb('draft');
$t->unsignedInteger('version')->default(1);
$t->uuid('published_version_id')->nullable();
$t->foreignId('created_by')->constrained('users')->restrictOnDelete();
$t->timestampsTz();
});
Schema::create('portal_tender_versions', function (Blueprint $t) {
$t->uuid('id')->primary();
$t->foreignUuid('tender_id')->constrained('portal_tenders')->restrictOnDelete();
$t->unsignedInteger('version');
$t->string('act', 24);
$t->jsonb('payload');
$t->text('reason');
$t->foreignId('actor_id')->constrained('users')->restrictOnDelete();
$t->timestampTz('published_at')->nullable();
$t->timestampTz('created_at')->useCurrent();
$t->unique(['tender_id', 'version']);
});
Schema::create('portal_tender_watches', function (Blueprint $t) {
$t->foreignUuid('tender_id')->constrained('portal_tenders')->restrictOnDelete();
$t->foreignId('user_id')->constrained('users')->restrictOnDelete();
$t->uuid('seen_version_id')->nullable();
$t->timestampsTz();
$t->primary(['tender_id', 'user_id']);
});
}
public function down(): void
{
if (DB::table('portal_tenders')->exists()) {
throw new RuntimeException('Preserve procedures, acts and history; use a reviewed data reversal.');
}
Schema::dropIfExists('portal_tender_watches');
Schema::dropIfExists('portal_tender_versions');
Schema::dropIfExists('portal_tenders');
}
};
+19
View File
@@ -0,0 +1,19 @@
FROM node:22-bookworm AS node
FROM php:8.4-cli-bookworm
RUN apt-get update \
&& apt-get install -y --no-install-recommends libpq-dev libzip-dev unzip git curl \
&& docker-php-ext-install -j"$(nproc)" pdo_pgsql pgsql zip \
&& rm -rf /var/lib/apt/lists/*
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \
&& ln -s ../lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --no-audit --no-fund
COPY . .
+13
View File
@@ -0,0 +1,13 @@
FROM php:8.4-cli-bookworm
RUN apt-get update \
&& apt-get install -y --no-install-recommends libpq-dev libzip-dev unzip git curl \
&& docker-php-ext-install -j"$(nproc)" pdo_pgsql pgsql zip \
&& rm -rf /var/lib/apt/lists/*
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
COPY composer.json composer.lock ./
RUN composer install --no-interaction --prefer-dist --no-progress --no-scripts
COPY . .
RUN composer dump-autoload --optimize
@@ -0,0 +1,176 @@
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="utf-8">
<title>InvestMoz — Levantamento funcional e modelos</title>
<style>
@page { size: A4; margin: 18mm 17mm 18mm 17mm; }
body { font-family: Aptos, Calibri, Arial, sans-serif; color:#1d2939; font-size:10.5pt; line-height:1.42; }
h1 { color:#123d68; font-size:26pt; margin:0 0 8pt; }
h2 { color:#123d68; font-size:17pt; border-bottom:2px solid #13a789; padding-bottom:5pt; margin-top:24pt; }
h3 { color:#145b82; font-size:13pt; margin:17pt 0 5pt; }
p { margin:5pt 0 8pt; }
.cover { padding-top:100pt; page-break-after:always; }
.kicker { text-transform:uppercase; letter-spacing:2px; color:#138a77; font-weight:bold; }
.subtitle { color:#52657a; font-size:15pt; }
.meta { margin-top:35pt; padding:12pt; background:#eff6fb; border-left:4px solid #1d75bd; }
.callout { background:#fff7e8; border-left:4px solid #ee9b20; padding:9pt 12pt; margin:12pt 0; }
.note { color:#52657a; font-size:9pt; }
table { border-collapse:collapse; width:100%; margin:8pt 0 14pt; font-size:9.2pt; }
th { background:#123d68; color:white; text-align:left; padding:6pt; }
td { border:1px solid #cbd5e1; padding:5pt 6pt; vertical-align:top; }
tr:nth-child(even) td { background:#f5f8fb; }
.diagram { text-align:center; margin:12pt 0; page-break-inside:avoid; }
.diagram svg { width:100%; height:auto; }
.legend { color:#52657a; font-size:9pt; }
.small { font-size:9pt; }
.pagebreak { page-break-before:always; }
ul { margin-top:3pt; }
</style>
</head>
<body>
<section class="cover">
<p class="kicker">Documento de análise funcional</p>
<h1>InvestMoz</h1>
<p class="subtitle">Levantamento profissional do projecto, casos de uso reais, actores e diagramas</p>
<div class="meta"><b>Data:</b> 9 de Outubro de 2026<br><b>Âmbito:</b> aplicação Laravel/React e documentação do repositório Investidor-V2<br><b>Versão:</b> 1.0 · análise do código e dos documentos disponíveis</div>
<p style="margin-top:42pt;font-size:13pt">Portal de descoberta de oportunidades e serviços de investimento, ligado a fluxos autenticados de candidatura, tratamento institucional, evidência documental e acompanhamento.</p>
<p class="note" style="margin-top:55pt">Documento técnico-funcional. A presença de uma rota, tabela, job ou descrição não demonstra, por si só, activação em produção.</p>
</section>
<h2>1. Sumário executivo</h2>
<p>O InvestMoz é uma plataforma digital que aproxima investidores, promotores e instituições. A experiência começa no portal público, onde se consultam sectores, oportunidades, negócios existentes, território, zonas e serviços. A partir daí, a pessoa pode manifestar interesse, iniciar uma simulação ou pedir apoio. Os percursos que exigem continuidade passam para a área autenticada, onde se mantêm perfil, rascunhos, candidaturas, anexos e comunicação.</p>
<p>Do lado institucional, os pedidos podem ser revistos e acompanhados por CRM local, tarefas, revisões e eventos. A plataforma também contém domínios de catálogo territorial, zonas de investimento, documentação, governação fiscal, educação e competências, assistência de terra, parcerias, balcão virtual e IA baseada em fontes.</p>
<div class="callout"><b>Leitura funcional:</b> trata-se de uma plataforma de encaminhamento e gestão de jornadas de investimento, e não apenas de um catálogo de anúncios. A camada pública informa e capta pedidos; a camada autenticada conserva o trabalho; a camada de gestão analisa, valida e decide segundo permissões e evidência.</div>
<p>O README descreve a base inicial de sectores, oportunidades e manifestações de interesse. Rotas, controladores, migrations e documentação de domínio mostram uma solução mais abrangente. O levantamento de casos detalhados tem 24 casos de uso; este relatório acrescenta cenários concretos, diagramas UML/ER e a leitura da frequência dos eventos.</p>
<h2>2. O que o projecto faz</h2>
<table><tr><th>Capacidade</th><th>Valor para o utilizador</th><th>Exemplos de dados</th></tr>
<tr><td>Descoberta pública</td><td>Encontrar oportunidades, negócios, sectores, serviços e referências territoriais.</td><td>sectors, opportunities, existing_businesses, investment_zones</td></tr>
<tr><td>Captação de interesse</td><td>Enviar manifestação associada a uma oportunidade ou negócio, com consentimento.</td><td>interest_requests, consents, receipts</td></tr>
<tr><td>Jornada do investidor</td><td>Guardar itens, criar dossier/submissão, anexar documentos e acompanhar mensagens.</td><td>investment_submissions, revisions, messages, attachments</td></tr>
<tr><td>Tratamento institucional</td><td>Qualificar pedidos, atribuir responsável, criar tarefas e acompanhar a negociação.</td><td>leads, contacts, crm_opportunities, tasks, activities</td></tr>
<tr><td>Planeamento territorial</td><td>Relacionar projectos, clusters, corredores, localizações e zonas.</td><td>projects, economic_clusters, development_corridors, geo_locations</td></tr>
<tr><td>Documentos e evidência</td><td>Preservar origem, versões, hash, scan, classificação e autorização de divulgação.</td><td>documents, document_versions, malware_scans, associations</td></tr>
<tr><td>Simulação e regras fiscais</td><td>Guardar rascunhos, executar simulações com parâmetros/fontes rastreáveis e governar versões.</td><td>simulations, scenarios, results, tax_rule_versions, legal_sources</td></tr>
<tr><td>Serviços complementares</td><td>Registar educação, assistência de terra, parcerias, balcão virtual e apoio com IA.</td><td>training needs, land cases, partnerships, desk cases, ai runs</td></tr></table>
<h2>3. Actores e fronteiras de acesso</h2>
<table><tr><th>Actor</th><th>O que pretende</th><th>Zona típica</th></tr>
<tr><td>Visitante</td><td>Consultar conteúdos públicos e iniciar interesse, apoio ou simulação.</td><td>Portal público</td></tr>
<tr><td>Investidor / utilizador registado</td><td>Manter perfil, favoritos, rascunhos, submissões, anexos e mensagens.</td><td>Área autenticada</td></tr>
<tr><td>Promotor / proprietário de negócio</td><td>Registar ou actualizar negócio e responder a pedidos de informação.</td><td>Área autenticada e workflow de revisão</td></tr>
<tr><td>Gestor de portal / CRM</td><td>Rever interesses, qualificar leads, atribuir tarefas e gerir conteúdos.</td><td>Gestão interna</td></tr>
<tr><td>Gestor territorial</td><td>Manter projectos, relações, zonas, geometria e referências territoriais.</td><td>Gestão interna</td></tr>
<tr><td>Revisor documental / gestor fiscal</td><td>Validar metadados, evidência, versões e regras sob as respectivas funções.</td><td>Gestão restrita</td></tr>
<tr><td>Agente de serviço / parceiro institucional</td><td>Tratar casos ou colaborar em parcerias e salas.</td><td>Serviços e colaboração</td></tr>
<tr><td>Administrador / worker</td><td>Gerir segurança/configuração ou executar trabalho assíncrono autorizado.</td><td>Administração / sistema</td></tr></table>
<p class="note">Os nomes acima são papéis funcionais consolidados. A autorização efectiva depende das policies, middleware, MFA, configuração e permissões em vigor.</p>
<h2>4. Diagrama de casos de uso com actores</h2>
<p>O diagrama destaca actores (“bonecos”) e os principais serviços que usam. As associações mostram interacções típicas; não indicam que todos os serviços estejam activos na mesma instalação.</p>
<div class="diagram">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1050 610" role="img" aria-label="Diagrama UML de casos de uso InvestMoz com actores">
<defs><style>.actor{stroke:#173b60;stroke-width:3;fill:none}.line{stroke:#728397;stroke-width:1.7}.uc{fill:#eef6fc;stroke:#2778b8;stroke-width:2}.txt{font:16px Arial;fill:#17324d}.label{font:bold 15px Arial;fill:#17324d}.boundary{fill:#fbfdff;stroke:#9bb0c3;stroke-width:2;stroke-dasharray:8 5}</style></defs>
<rect x="220" y="22" width="795" height="558" rx="14" class="boundary"/><text x="245" y="52" class="label">Sistema InvestMoz</text>
<!-- stick actors -->
<g class="actor"><circle cx="95" cy="115" r="17"/><path d="M95 132v65m-30-38h60m-30 38-27 42m27-42 27 42"/></g><text x="50" y="245" class="label">Visitante</text>
<g class="actor"><circle cx="95" cy="335" r="17"/><path d="M95 352v65m-30-38h60m-30 38-27 42m27-42 27 42"/></g><text x="37" y="465" class="label">Investidor</text>
<g class="actor"><circle cx="95" cy="510" r="17"/><path d="M95 527v45m-30-25h60m-30 25-22 33m22-33 22 33"/></g><text x="15" y="606" class="label">Gestor</text>
<g class="actor"><circle cx="1025" cy="165" r="15"/><path d="M1025 180v55m-25-33h50m-25 33-22 36m22-36 22 36"/></g><text x="979" y="267" class="label">Revisor</text>
<g class="actor"><circle cx="1025" cy="400" r="15"/><path d="M1025 415v55m-25-33h50m-25 33-22 36m22-36 22 36"/></g><text x="967" y="502" class="label">Parceiro</text>
<!-- use cases -->
<g><ellipse cx="410" cy="112" rx="145" ry="31" class="uc"/><text x="410" y="118" text-anchor="middle" class="txt">Consultar catálogo e mapa</text>
<ellipse cx="730" cy="112" rx="145" ry="31" class="uc"/><text x="730" y="118" text-anchor="middle" class="txt">Pesquisar oportunidade/negócio</text>
<ellipse cx="410" cy="205" rx="145" ry="31" class="uc"/><text x="410" y="211" text-anchor="middle" class="txt">Manifestar interesse</text>
<ellipse cx="730" cy="205" rx="145" ry="31" class="uc"/><text x="730" y="211" text-anchor="middle" class="txt">Executar simulação</text>
<ellipse cx="410" cy="300" rx="145" ry="31" class="uc"/><text x="410" y="306" text-anchor="middle" class="txt">Criar candidatura / dossier</text>
<ellipse cx="730" cy="300" rx="145" ry="31" class="uc"/><text x="730" y="306" text-anchor="middle" class="txt">Anexar e acompanhar documentos</text>
<ellipse cx="410" cy="397" rx="145" ry="31" class="uc"/><text x="410" y="403" text-anchor="middle" class="txt">Tratar interesse no CRM</text>
<ellipse cx="730" cy="397" rx="145" ry="31" class="uc"/><text x="730" y="403" text-anchor="middle" class="txt">Validar e publicar conteúdo</text>
<ellipse cx="410" cy="495" rx="145" ry="31" class="uc"/><text x="410" y="501" text-anchor="middle" class="txt">Gerir território e zonas</text>
<ellipse cx="730" cy="495" rx="145" ry="31" class="uc"/><text x="730" y="501" text-anchor="middle" class="txt">Colaborar em parceria/serviço</text></g>
<!-- actor associations -->
<g class="line"><path d="M112 115H264M112 115 585 112M112 145 265 195M112 355 265 300M112 370 265 300M112 388 585 300M112 515 265 397M112 525 265 495M112 540 585 495M1008 175 875 397M1008 190 875 300M1008 410 875 495"/></g>
</svg></div>
<p class="legend">Exemplo do percurso mais comum: visitante pesquisa → manifesta interesse → investidor cria/acompanha candidatura → gestão trata o pedido → revisão e decisão.</p>
<h2>5. Casos de uso reais — jornadas ilustrativas</h2>
<h3>UC-A — Investidor encontra oportunidade e manifesta interesse</h3>
<ol><li>O visitante consulta sectores, filtra oportunidades por localização e abre o detalhe.</li><li>Escolhe “manifestar interesse”, informa contacto, modalidade e mensagem, e aceita o consentimento aplicável.</li><li>O sistema valida o alvo, regista o pedido e, quando configurado, um recibo/idempotência.</li><li>O investidor pode autenticar-se para guardar a oportunidade ou avançar para uma submissão.</li><li>Um gestor consulta o pedido, verifica consentimentos, atribui responsável, qualifica e cria tarefa de seguimento.</li><li>As alterações relevantes ficam no histórico; notificações e integrações só são consideradas entregues quando o disparo e o serviço estiverem confirmados.</li></ol>
<p><b>Resultado:</b> pedido rastreável ligado à oportunidade e encaminhado para tratamento. <b>Entidades:</b> Opportunity, Investor/User, InterestRequest, Consent, Lead, Task, AuditEvent.</p>
<h3>UC-B — Proprietário submete um negócio para divulgação</h3>
<ol><li>O proprietário cria rascunho e informa modalidade, objectos, localização e documentos de suporte.</li><li>Submete o registo; o estado passa para revisão conforme o workflow aplicável.</li><li>O gestor analisa e pode aprovar, rejeitar ou pedir informação complementar.</li><li>O revisor documental confere original, hash, versão, scan e autorização de divulgação.</li><li>Só após as validações necessárias o conteúdo pode ser publicado ao público; publicação não altera a situação jurídica do documento.</li></ol>
<p><b>Resultado:</b> negócio com histórico e decisão rastreável. <b>Estados documentados:</b> draft → submitted → in_review → approved → published, com ramos needs_information, rejected, withdrawn, suspended e archived.</p>
<h3>UC-C — Gestor territorial valida zona e relações</h3>
<ol><li>O gestor regista a zona e a referência/fonte pertinente.</li><li>Associa localização, operador, categoria, geometria e relações com projectos, clusters ou corredores.</li><li>As relações sem evidência verificada permanecem pendentes e identificadas.</li><li>Um revisor valida a evidência e as regras de publicação; alterações ficam versionadas.</li><li>A zona só aparece no portal conforme estado e autorização de divulgação.</li></ol>
<h3>UC-D — Utilizador guarda um rascunho e executa simulação</h3>
<ol><li>O utilizador preenche o formulário; os dados incompletos podem ser guardados como rascunho recuperável.</li><li>Quando solicita o cálculo, o sistema selecciona questionário e parâmetros elegíveis.</li><li>O job de simulação executa após a transacção e grava cenários, resultados, obrigações e fontes usadas.</li><li>O relatório pode ser consultado; o rascunho continua distinto do cálculo executado.</li></ol>
<div class="callout"><b>Regra de integridade:</b> importar ou publicar fonte documental não activa automaticamente uma regra fiscal. Parâmetros ausentes/ambíguos permanecem indeterminados.</div>
<h3>UC-E — Parceiros colaboram numa parceria ou serviço</h3>
<p>Um actor cria uma proposta; outro manifesta interesse; após aceitação, os participantes podem colaborar numa sala, trocar mensagens/documentos e acompanhar marcos. O balcão virtual segue padrão semelhante para pedidos de serviço: registo, análise, pedido de documentos, eventos, revisão e resolução.</p>
<h2>6. Diagrama de classes (visão lógica)</h2>
<p>O diagrama apresenta as classes de domínio principais, atributos representativos e operações. É uma vista funcional; não reproduz todos os campos nem métodos Laravel.</p>
<div class="diagram"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 690" role="img" aria-label="Diagrama UML de classes InvestMoz">
<defs><style>.box{fill:#fff;stroke:#276c9b;stroke-width:2}.head{fill:#e8f2f9}.ct{font:14px Arial;fill:#17324d}.ch{font:bold 15px Arial;fill:#123d68}.rel{stroke:#60768b;stroke-width:1.8;fill:none}.mult{font:12px Arial;fill:#455b70}</style></defs>
<!-- classes -->
<g><rect x="30" y="30" width="220" height="120" rx="5" class="box"/><rect x="30" y="30" width="220" height="32" class="head"/><text x="140" y="52" text-anchor="middle" class="ch">User</text><text x="44" y="82" class="ct">id, email, verified_at</text><text x="44" y="105" class="ct">2FA / passkeys</text><text x="44" y="132" class="ct">+ authenticate()</text>
<rect x="315" y="30" width="220" height="120" rx="5" class="box"/><rect x="315" y="30" width="220" height="32" class="head"/><text x="425" y="52" text-anchor="middle" class="ch">Investor</text><text x="329" y="82" class="ct">id, user_id, profile</text><text x="329" y="105" class="ct">company interests</text><text x="329" y="132" class="ct">+ manageProfile()</text>
<rect x="600" y="30" width="220" height="120" rx="5" class="box"/><rect x="600" y="30" width="220" height="32" class="head"/><text x="710" y="52" text-anchor="middle" class="ch">Opportunity</text><text x="614" y="82" class="ct">sector, slug, status</text><text x="614" y="105" class="ct">location, investment</text><text x="614" y="132" class="ct">+ publish()</text>
<rect x="865" y="30" width="205" height="120" rx="5" class="box"/><rect x="865" y="30" width="205" height="32" class="head"/><text x="967" y="52" text-anchor="middle" class="ch">Sector</text><text x="879" y="82" class="ct">id, name, slug</text><text x="879" y="105" class="ct">active / published</text><text x="879" y="132" class="ct">+ listOpportunities()</text>
<rect x="30" y="230" width="235" height="130" rx="5" class="box"/><rect x="30" y="230" width="235" height="32" class="head"/><text x="147" y="252" text-anchor="middle" class="ch">InterestRequest</text><text x="44" y="282" class="ct">target, contact, consent</text><text x="44" y="305" class="ct">status, submitted_at</text><text x="44" y="338" class="ct">+ submit() / qualify()</text>
<rect x="325" y="230" width="235" height="130" rx="5" class="box"/><rect x="325" y="230" width="235" height="32" class="head"/><text x="442" y="252" text-anchor="middle" class="ch">Lead / Task</text><text x="339" y="282" class="ct">owner, next_action</text><text x="339" y="305" class="ct">due_at, status</text><text x="339" y="338" class="ct">+ assign() / complete()</text>
<rect x="620" y="230" width="235" height="130" rx="5" class="box"/><rect x="620" y="230" width="235" height="32" class="head"/><text x="737" y="252" text-anchor="middle" class="ch">Submission</text><text x="634" y="282" class="ct">owner, target, state</text><text x="634" y="305" class="ct">revisions, needs</text><text x="634" y="338" class="ct">+ save() / submit()</text>
<rect x="910" y="230" width="160" height="130" rx="5" class="box"/><rect x="910" y="230" width="160" height="32" class="head"/><text x="990" y="252" text-anchor="middle" class="ch">Document</text><text x="924" y="282" class="ct">hash, source</text><text x="924" y="305" class="ct">version, status</text><text x="924" y="338" class="ct">+ scan()</text>
<rect x="30" y="450" width="235" height="130" rx="5" class="box"/><rect x="30" y="450" width="235" height="32" class="head"/><text x="147" y="472" text-anchor="middle" class="ch">Project / Zone</text><text x="44" y="502" class="ct">territory, geometry</text><text x="44" y="525" class="ct">relations, evidence</text><text x="44" y="558" class="ct">+ validate() / publish()</text>
<rect x="325" y="450" width="235" height="130" rx="5" class="box"/><rect x="325" y="450" width="235" height="32" class="head"/><text x="442" y="472" text-anchor="middle" class="ch">Simulation</text><text x="339" y="502" class="ct">questionnaire, input</text><text x="339" y="525" class="ct">scenario, result, sources</text><text x="339" y="558" class="ct">+ execute() / report()</text>
<rect x="620" y="450" width="235" height="130" rx="5" class="box"/><rect x="620" y="450" width="235" height="32" class="head"/><text x="737" y="472" text-anchor="middle" class="ch">TaxRuleVersion</text><text x="634" y="502" class="ct">source, dates, parameters</text><text x="634" y="525" class="ct">review / approval state</text><text x="634" y="558" class="ct">+ resolveForPeriod()</text>
<rect x="910" y="450" width="160" height="130" rx="5" class="box"/><rect x="910" y="450" width="160" height="32" class="head"/><text x="990" y="472" text-anchor="middle" class="ch">AuditEvent</text><text x="924" y="502" class="ct">actor, entity</text><text x="924" y="525" class="ct">action, time</text><text x="924" y="558" class="ct">+ record()</text></g>
<!-- relations -->
<g class="rel"><path d="M250 90H315M535 90H600M820 90H865M140 150V230M425 150V230M710 150V230M265 295H325M560 295H620M855 295H910M140 360V450M442 360V450M737 360V450M855 515H910M250 130 900 470"/></g>
<g class="mult"><text x="270" y="82">1 : 0..1</text><text x="547" y="82">0..*</text><text x="834" y="82">1</text><text x="148" y="205">1 : 0..*</text><text x="273" y="286">0..1</text><text x="567" y="286">1..*</text><text x="861" y="286">0..*</text><text x="145" y="435">1 : 0..*</text><text x="450" y="435">1 : 0..*</text></g>
</svg></div>
<p class="note">A relação exacta Investor/User, a associação Submission/target e os vínculos Document/entidade podem ser opcionais ou implementados por tabelas polimórficas/de junção; confirme no schema antes de gerar código ou migrações.</p>
<h2>7. Diagrama de relacionamento (ER simplificado)</h2>
<div class="diagram"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1080 560" role="img" aria-label="Diagrama de relacionamento das entidades InvestMoz">
<defs><style>.e{fill:#edf5fb;stroke:#2477b6;stroke-width:2}.et{font:bold 15px Arial;fill:#123d68}.r{stroke:#61788d;stroke-width:2;fill:none}.rt{font:12px Arial;fill:#40566b}</style></defs>
<g><rect x="35" y="45" width="155" height="60" rx="9" class="e"/><text x="112" y="81" text-anchor="middle" class="et">SECTOR</text><rect x="260" y="45" width="180" height="60" rx="9" class="e"/><text x="350" y="81" text-anchor="middle" class="et">OPPORTUNITY</text><rect x="515" y="45" width="190" height="60" rx="9" class="e"/><text x="610" y="81" text-anchor="middle" class="et">INTEREST_REQUEST</text><rect x="805" y="45" width="160" height="60" rx="9" class="e"/><text x="885" y="81" text-anchor="middle" class="et">INVESTOR</text>
<rect x="35" y="205" width="155" height="60" rx="9" class="e"/><text x="112" y="241" text-anchor="middle" class="et">USER</text><rect x="260" y="205" width="180" height="60" rx="9" class="e"/><text x="350" y="241" text-anchor="middle" class="et">SUBMISSION</text><rect x="515" y="205" width="190" height="60" rx="9" class="e"/><text x="610" y="241" text-anchor="middle" class="et">DOCUMENT</text><rect x="805" y="205" width="160" height="60" rx="9" class="e"/><text x="885" y="241" text-anchor="middle" class="et">AUDIT_EVENT</text>
<rect x="35" y="365" width="155" height="60" rx="9" class="e"/><text x="112" y="401" text-anchor="middle" class="et">PROJECT</text><rect x="260" y="365" width="180" height="60" rx="9" class="e"/><text x="350" y="401" text-anchor="middle" class="et">CLUSTER</text><rect x="515" y="365" width="190" height="60" rx="9" class="e"/><text x="610" y="401" text-anchor="middle" class="et">INVESTMENT_ZONE</text><rect x="805" y="365" width="160" height="60" rx="9" class="e"/><text x="885" y="401" text-anchor="middle" class="et">LEGAL_SOURCE</text>
<rect x="145" y="490" width="180" height="55" rx="9" class="e"/><text x="235" y="523" text-anchor="middle" class="et">SIMULATION</text><rect x="450" y="490" width="180" height="55" rx="9" class="e"/><text x="540" y="523" text-anchor="middle" class="et">TAX_RULE_VERSION</text><rect x="755" y="490" width="180" height="55" rx="9" class="e"/><text x="845" y="523" text-anchor="middle" class="et">SIMULATION_RESULT</text></g>
<g class="r"><path d="M190 75H260M440 75H515M705 75H805M190 235H260M440 235H515M705 235H805M190 395H260M440 395H515M705 395H805M235 425V490M325 518H450M630 518H755M350 105V205M610 105V205M610 265V365M885 105V205M885 425V455H540V490"/></g>
<g class="rt"><text x="210" y="67">1 : N</text><text x="465" y="67">1 : N</text><text x="730" y="67">N : 0..1</text><text x="210" y="227">1 : N</text><text x="462" y="227">N : M* via links</text><text x="730" y="227">1 : N</text><text x="210" y="387">N : M*</text><text x="462" y="387">N : M*</text><text x="730" y="387">1 : N</text><text x="235" y="466">1 : N</text><text x="346" y="510">1 : N</text><text x="650" y="510">1 : N</text><text x="354" y="160">1 : N</text><text x="617" y="160">association</text><text x="894" y="160">actor</text><text x="670" y="448">source</text></g>
</svg></div>
<p class="note">N:M = muitos-para-muitos, normalmente resolvido por tabela de associação. Ligações genéricas de documentos/auditoria podem não ser FKs directas. Este mapa é uma vista funcional, não um substituto do ERD integral em docs/investmoz/erd.mmd e do dicionário de dados.</p>
<h2>8. Eventos e frequência</h2>
<table><tr><th>Evento/processo</th><th>Gatilho</th><th>Frequência confirmada</th><th>Leitura operacional</th></tr>
<tr><td>Escalar tarefas vencidas</td><td>Scheduler Laravel</td><td>A cada 15 minutos; sem sobreposição configurada</td><td>Cadência declarada em routes/console.php. O scheduler do servidor tem de estar activo.</td></tr>
<tr><td>Executar simulação (RunSimulation)</td><td>Pedido de cálculo aceite</td><td>Por pedido, via fila, afterCommit</td><td>Assíncrono; volume depende da utilização e dos workers.</td></tr>
<tr><td>Scan de malware (ScanDocumentForMalware)</td><td>Upload ou pedido de análise em fluxos suportados</td><td>Por documento, via fila; job único</td><td>Depende de worker e scanner. Upload não prova análise concluída.</td></tr>
<tr><td>Comparação de compatibilidade MNE</td><td>Alteração aplicável do negócio</td><td>Por alteração aplicável, em fila de sombra</td><td>Job identificado; frequência de alterações não medida.</td></tr>
<tr><td>Notificação de interesse</td><td>Recepção de interesse</td><td>Disparo não confirmado</td><td>Job NotifyInterestReceived existe, mas não foi localizada chamada dispatch().</td></tr>
<tr><td>Outbox/inbox e sincronização externa</td><td>Evento/mensagem externa</td><td>Sem calendário operacional confirmado</td><td>Estrutura preparada; documentação indica sincronização real pendente.</td></tr>
<tr><td>Pesquisas, submissões, revisões e auditoria</td><td>Acção do visitante/utilizador/gestor</td><td>Sob procura ou acção; contagem indisponível</td><td>Sem logs/analytics não se calculam ocorrências por período.</td></tr></table>
<p><b>Frequência configurada não é frequência observada.</b> Não foram disponibilizados logs, contagens de eventos da base activa, filas, analytics nem tráfego HTTP. Assim, eventos por hora/dia/mês, sazonalidade, falhas e utilizadores activos permanecem indeterminados. Não se extrapolam estes valores a partir de testes, seeders, screenshots ou schema.</p>
<h2>9. Inventário dos 24 casos de uso</h2>
<table><tr><th>Grupo</th><th>Casos</th></tr>
<tr><td>Portal e catálogo</td><td>UC-01 Consultar página inicial e serviços; UC-02 Pesquisar sectores e oportunidades; UC-03 Consultar negócio existente.</td></tr>
<tr><td>Conta e jornada</td><td>UC-04 Criar conta/autenticar/recuperar acesso; UC-05 Manifestar interesse numa oportunidade; UC-06 Manifestar interesse num negócio; UC-07 Guardar oportunidade; UC-08 Manter submissão; UC-09 Submeter candidatura.</td></tr>
<tr><td>Negócios e CRM</td><td>UC-10 Gerir negócio existente; UC-11 Tratar interesses no CRM.</td></tr>
<tr><td>Território e documentos</td><td>UC-12 Gerir catálogo territorial; UC-13 Gerir zonas; UC-14 Carregar, verificar e publicar documento.</td></tr>
<tr><td>Simulação/fiscalidade</td><td>UC-15 Criar rascunho; UC-16 Executar simulação; UC-17 Gerir regra fiscal.</td></tr>
<tr><td>Serviços especializados</td><td>UC-18 Gerir educação e competências; UC-19 Assistência de terra; UC-20 Gerir parceria; UC-21 Usar balcão virtual.</td></tr>
<tr><td>Transversal</td><td>UC-22 Usar IA com evidência; UC-23 Sincronizar sistemas externos; UC-24 Auditar alterações.</td></tr></table>
<p>Os pré-requisitos, fluxos alternativos e tabelas associadas por UC encontram-se no ficheiro <i>casos-de-uso-aplicacao-actual.md</i>.</p>
<h2>10. Integridade, segurança e limitações</h2>
<ul><li>Disponibilidade para consulta, situação jurídica/documental e uso em regra/cálculo são estados independentes.</li><li>Respeitar autorização de divulgação e âmbito; relações sem evidência ficam pendentes.</li><li>Preservar original, proveniência, ID exacto, edição/data, hash e histórico. Não confirmar autenticidade, vigência, direitos ou scan sem evidência.</li><li>Importação não é publicação; publicação documental não activa cálculo fiscal; rascunho não é candidatura aprovada.</li><li>Integração desenhada não significa sincronização activa. Cobertura e estado de cada domínio devem ser verificados nos respectivos relatórios de validação.</li><li>Antes de alterações a dados activos, confirmar backup/restauro e acção autenticada. Testes e migrações usam exclusivamente base QA isolada.</li></ul>
<h2>11. Fontes de análise</h2>
<ul><li>README.md do repositório;</li><li>docs/investmoz/casos-de-uso-aplicacao-actual.md;</li><li>docs/investmoz/erd.mmd, data-dictionary.csv e table-catalog.csv;</li><li>rotas, controladores, modelos, jobs, comando de escalada e migrations;</li><li>InvestMoz — Levantamento funcional, casos de uso e diagramas.docx, em Downloads.</li></ul>
<p class="note">O inventário cobriu 981 caminhos listados por rg --files, excluindo vendor e node_modules. Isso descreve o repositório acessível, não anexos externos, conteúdo de armazenamento privado ou base de dados de produção.</p>
</body></html>
@@ -0,0 +1,526 @@
# Casos de uso da aplicação actual
Este documento descreve os processos actualmente implementados ou previstos
pelas rotas, controladores, serviços e migrations existentes no projecto.
Não substitui regras legais nem autoriza publicação de dados.
## 1. Actores
| Actor | Responsabilidade |
|---|---|
| Visitante | Consulta o portal, oportunidades, negócios, zonas e documentos públicos. |
| Utilizador registado | Mantém perfil, rascunhos, submissões, favoritos e mensagens. |
| Investidor | Pesquisa oportunidades, manifesta interesse e acompanha candidaturas. |
| Gestor | Trata interesses, leads, tarefas, oportunidades e catálogo. |
| Revisor documental | Verifica documentos, malware, versões e autorização de divulgação. |
| Gestor territorial | Gere projectos, clusters, corredores, geografia e zonas. |
| Gestor fiscal | Gere fontes legais, versões de regras e parâmetros do simulador. |
| Gestor de educação | Gere competências, necessidades, cursos e propostas. |
| Parceiro institucional | Participa em parcerias, salas, mensagens, documentos e marcos. |
| Administrador | Gere utilizadores, permissões, configurações e instalação de módulos. |
| Sistema externo | Recebe/envi­a eventos através de outbox, inbox e sincronizações. |
## 2. Visão geral do ciclo da aplicação
```text
Portal público
-> consulta de catálogo
-> interesse, apoio ou simulação
-> autenticação/conta quando necessário
-> candidatura ou submissão
-> documentos e validação
-> CRM, tarefas e comunicação
-> decisão, parceria ou acompanhamento
```
Os principais agregados são `opportunities`, `existing_businesses`,
`interest_requests`, `investment_submissions`, `documents`, `investment_zones`
e `simulations`.
## 3. UC-01 — Consultar a página inicial e serviços
**Actor:** Visitante.
**Pré-condições:** O portal está disponível.
**Fluxo principal:**
1. O visitante abre `/`.
2. O sistema apresenta a página inicial e os serviços publicados.
3. O visitante escolhe oportunidades, negócios, zonas, simulador, documentos,
apoio ou parcerias.
4. O sistema aplica apenas dados que estejam disponíveis para o público.
**Dados:** `portal_resources`, `documents`, `opportunities`, `sectors`,
`existing_businesses`, `investment_zones`.
**Resultado:** O visitante é encaminhado para o processo escolhido.
## 4. UC-02 — Pesquisar sectores e oportunidades
**Actor:** Visitante ou investidor.
**Fluxo principal:**
1. O actor consulta `/oportunidades` ou a API de oportunidades.
2. O sistema filtra por sector, localização, slug, estado e outros critérios.
3. O detalhe da oportunidade mostra perfil, território, relações, documentos
públicos e informação de investimento.
4. O actor pode iniciar uma manifestação de interesse ou guardar a oportunidade.
**Dados:** `sectors`, `opportunities`, `opportunity_profiles`,
`opportunity_facets`, `opportunity_clusters`, `opportunity_corridors`,
`opportunity_projects`, `opportunity_locations`, `opportunity_favourites`.
**Resultado:** Consulta pública ou início de UC-05/UC-06.
## 5. UC-03 — Consultar um negócio existente
**Actor:** Visitante ou investidor.
**Fluxo principal:**
1. O actor consulta `/negocios`.
2. O sistema lista negócios cujo estado e autorização permitem divulgação.
3. O actor abre o detalhe pelo `slug`.
4. O sistema apresenta apenas título, descrição, modalidade, localização,
objectos e documentos autorizados.
5. O actor pode iniciar uma manifestação de interesse.
**Dados:** `existing_businesses`, `existing_business_objects`,
`existing_business_locations`, `existing_business_document_links`,
`documents`, `interest_requests`.
**Regra:** Dados privados, mandatos e documentos não autorizados não são
expostos pelo portal público.
## 6. UC-04 — Criar conta, autenticar e recuperar acesso
**Actor:** Visitante/utilizador.
**Fluxo principal:**
1. O visitante cria uma conta com nome, email e palavra-passe.
2. O sistema cria `users`.
3. O utilizador confirma o email quando aplicável.
4. O utilizador inicia sessão.
5. O sistema aplica as políticas de autenticação, confirmação de palavra-passe
e, para operações protegidas, segundo factor.
**Dados:** `users`, `passkeys`, `sessions`, `password_reset_tokens`.
**Alternativas:** Registo rejeitado por email existente; palavra-passe
incorrecta; recuperação por token; desafio de dois factores.
## 7. UC-05 — Manifestar interesse numa oportunidade
**Actor:** Visitante ou investidor.
**Fluxo principal:**
1. O actor abre o formulário de interesse.
2. Informa contacto, oportunidade, modalidade, valor e mensagem.
3. O sistema valida os dados, consentimentos e alvo.
4. É criado `interest_requests`.
5. O sistema regista recibo/idempotência quando aplicável.
6. O sistema envia notificação ou evento de tratamento.
**Dados:** `interest_requests`, `consents`, `interest_submission_receipts`,
`notifications`, `outbox_events`.
**Estado inicial:** `received`, `submitted` ou equivalente configurado.
**Resultado:** O interesse fica disponível para tratamento no CRM.
## 8. UC-06 — Manifestar interesse num negócio existente
**Actor:** Investidor.
**Fluxo principal:**
1. O investidor escolhe um `existing_business` publicado.
2. Selecciona a modalidade e, se aplicável, percentagem e intervalo de valor.
3. O sistema verifica que o negócio pode receber interesse.
4. Cria `interest_requests.existing_business_id`.
5. Regista consentimento para contacto e partilha da informação.
6. O caso passa para tratamento interno.
**Dados:** `existing_businesses`, `interest_requests`, `investors`, `consents`.
**Regra:** Um pedido não pode apontar simultaneamente para uma oportunidade e
para um negócio existente.
## 9. UC-07 — Guardar uma oportunidade e manter espaço do investidor
**Actor:** Utilizador autenticado.
**Fluxo principal:**
1. O utilizador abre uma oportunidade.
2. Escolhe guardar/iniciar no espaço do investidor.
3. O sistema regista a relação em `opportunity_favourites` ou inicia uma
submissão/dossier.
4. O utilizador consulta os seus itens em `/investidor` ou `/carteira`.
**Dados:** `users`, `opportunity_favourites`, `investment_dossiers`,
`investor_accounts`, `investment_accounts`.
## 10. UC-08 — Criar e manter uma submissão de investimento
**Actor:** Investidor.
**Pré-condições:** Utilizador autenticado e verificado.
**Fluxo principal:**
1. O investidor cria uma submissão.
2. O sistema cria `investment_submissions` com estado `draft`.
3. O investidor grava o payload e as necessidades específicas.
4. Pode adicionar anexos e mensagens.
5. O sistema cria eventos e revisões para manter histórico.
6. O investidor consulta ou actualiza a submissão.
**Dados:** `investment_submissions`, `investment_specific_needs`,
`investment_submission_attachments`, `investment_submission_events`,
`investment_submission_revisions`, `investment_messages`, `documents`.
## 11. UC-09 — Submeter candidatura
**Actor:** Investidor.
**Fluxo principal:**
1. O investidor confirma os dados da submissão.
2. O sistema verifica campos obrigatórios, alvo e anexos.
3. A submissão muda de `draft` para `submitted`.
4. É associado ou criado o pedido de interesse, quando exigido.
5. O sistema cria evento de submissão e notifica a gestão.
**Resultado:** A candidatura deixa de ser apenas rascunho e entra na fila de
tratamento interno.
## 12. UC-10 — Gerir um negócio existente
**Actor:** Investidor, proprietário ou gestor.
**Fluxo principal:**
1. O actor cria ou actualiza o negócio.
2. Regista modalidade, descrição, objectos, localização e documentos.
3. O sistema cria revisões e mantém o snapshot anterior.
4. Um gestor pode solicitar informação, rever ou rejeitar.
5. O negócio percorre o workflow configurado.
6. Apenas após validação e autorização poderá ser publicado.
**Dados:** `existing_businesses`, `existing_business_objects`,
`existing_business_revisions`, `existing_business_reviews`,
`business_workflow_transitions`, `existing_business_mandates`, `documents`.
**Estados possíveis:** `draft`, `submitted`, `in_review`,
`needs_information`, `approved`, `published`, `suspended`, `rejected`,
`withdrawn`, `archived`.
## 13. UC-11 — Tratar interesses no CRM
**Actor:** Gestor.
**Fluxo principal:**
1. O gestor abre a fila de interesses.
2. Consulta o pedido, consentimentos e alvo.
3. Atribui o caso a um responsável.
4. Converte o interesse em `leads`, quando aplicável.
5. Cria tarefas e actividades.
6. Actualiza o estado e regista o resultado.
7. Pode associar um `crm_opportunity` e continuar a negociação.
**Dados:** `interest_requests`, `leads`, `investors`, `contacts`,
`crm_opportunities`, `tasks`, `activities`, `consents`.
**Estados de interesse:** recebimento, qualificação, contacto, diligência,
negociação, conversão, recusa, cancelamento ou encerramento.
## 14. UC-12 — Gerir catálogo territorial
**Actor:** Gestor territorial.
**Fluxo principal:**
1. O gestor cria ou actualiza projectos, clusters e corredores.
2. Liga projectos a locais.
3. Liga projectos a clusters e corredores.
4. Cria cadeias de valor e lacunas de investimento.
5. Regista a fonte e o estado da relação.
6. Outro gestor pode validar ou revogar a relação.
**Dados:** `projects`, `economic_clusters`, `value_chains`,
`value_chain_links`, `development_corridors`, `cluster_projects`,
`corridor_projects`, `corridor_clusters`, `investment_gaps`, `geo_locations`.
## 15. UC-13 — Gerir zonas de investimento
**Actor:** Gestor territorial com permissão e MFA quando exigido.
**Fluxo principal:**
1. Regista a fonte legal em `investment_legal_sources`.
2. Cria a zona em `investment_zones`.
3. Associa operador, categoria e localização.
4. Adiciona geometria, anexos e relações com projectos, clusters ou corredores.
5. Um revisor valida a informação.
6. A zona é publicada, suspensa ou revogada.
7. O sistema mantém revisões imutáveis.
**Estados:** `draft`, `published`, `suspended`; validação `pending`,
`validated`, `historical`.
**Dados:** `investment_zones`, `investment_legal_sources`,
`investment_zone_geometries`, `investment_zone_relations`,
`investment_zone_links`, `investment_zone_attachments`, `investment_zone_revisions`.
## 16. UC-14 — Carregar, verificar e publicar documento
**Actor:** Gestor documental.
**Fluxo principal:**
1. O gestor carrega o ficheiro.
2. O sistema preserva o original e calcula o hash.
3. Detecta duplicados e conflitos de versão.
4. Regista `documents` e, quando necessário, `document_versions`.
5. Executa análise antimalware.
6. O gestor revê classificação, datas, fonte e autorização.
7. O documento é associado ao negócio, candidatura, zona, projecto ou portal.
8. Só então pode ser disponibilizado publicamente.
**Dados:** `documents`, `document_versions`, `document_malware_scans`,
`portal_resources`, `portal_resource_revisions` e tabelas `*_documents`.
**Estados documentais:** pendente, limpo, infectado, rejeitado, privado,
publicável, publicado ou revogado.
## 17. UC-15 — Criar rascunho de simulação
**Actor:** Visitante ou utilizador.
**Fluxo principal:**
1. O actor abre o simulador.
2. O sistema cria ou recupera um cookie de recuperação.
3. Guarda os dados em `simulation_form_drafts`.
4. O actor pode continuar mais tarde.
5. Um utilizador autenticado pode reclamar o rascunho.
6. O rascunho pode ser ligado a um negócio existente, mediante autorização.
**Resultado:** Dados incompletos permanecem como rascunho e não são tratados
como cálculo fiscal executado.
## 18. UC-16 — Executar simulação
**Actor:** Visitante ou utilizador.
**Fluxo principal:**
1. O actor submete os dados do simulador.
2. O sistema escolhe a versão do questionário.
3. Resolve os parâmetros fiscais elegíveis.
4. Cria `simulations` e `simulation_scenarios`.
5. Executa o cálculo através de job.
6. Guarda resultados, obrigações e fontes.
7. Gera relatório quando solicitado.
8. Regista auditoria e idempotência.
**Dados:** `questionnaire_versions`, `simulations`, `simulation_scenarios`,
`simulation_results`, `simulation_obligations`, `simulation_reports`,
`simulation_jobs`, `simulation_result_sources`, `simulation_audit_events`.
## 19. UC-17 — Gerir regra fiscal
**Actor:** Autor, revisor, aprovador ou publicador fiscal.
**Fluxo principal:**
1. O autor cria uma versão da regra.
2. Associa a regra a uma fonte legal.
3. O revisor verifica expressão, datas e parâmetros.
4. O aprovador decide.
5. O publicador disponibiliza a versão.
6. O sistema regista cada transição.
**Dados:** `simulation_tax_rule_versions`, `simulation_tax_rule_events`,
`investment_legal_sources`, `users`.
**Regra:** Importar ou publicar um documento não activa automaticamente um
cálculo fiscal.
## 20. UC-18 — Gerir educação e competências
**Actor:** Gestor de educação.
**Fluxo principal:**
1. Mantém catálogo de competências, profissões, instituições e cursos.
2. Regista necessidades de formação de projectos.
3. Associa ocupações e competências.
4. Regista capacidades e oportunidades educativas.
5. Analisa lacunas.
6. Cria proposta de investimento educativo.
**Dados:** `skills`, `occupations`, `training_needs`, `training_providers`,
`courses`, `education_opportunities`, `investment_gaps`,
`education_investment_proposals` e tabelas `skills_edu`.
## 21. UC-19 — Abrir e tratar assistência de terra
**Actor:** Investidor e gestor de assistência.
**Fluxo principal:**
1. O investidor cria um caso de assistência.
2. Regista localização, necessidade e documentos.
3. O gestor analisa o caso.
4. São registadas revisões, eventos e pedidos adicionais.
5. O caso é encaminhado ou encerrado.
**Dados:** `land_assistance_cases`, `land_assistance_revisions`,
`land_assistance_events`, `land_assistance_documents`, `documents`, `users`.
## 22. UC-20 — Criar e gerir parceria
**Actor:** Investidor, organização ou parceiro institucional.
**Fluxo principal:**
1. O actor cria um rascunho de parceria.
2. Regista origem, sector, local e objectivo.
3. Outro actor manifesta interesse.
4. A candidatura é aceite ou recusada.
5. É aberta uma sala de parceria.
6. Os participantes trocam mensagens e documentos.
7. São definidos marcos, pagamentos ou acordo.
8. O sistema regista eventos e notificações.
**Dados:** `portal_partnerships`, `partnership_origins`,
`partnership_locations`, `partnership_interests`, `partnership_rooms`,
`partnership_messages`, `partnership_documents`, `partnership_milestones`,
`partnership_events`, `partnership_notifications`.
## 23. UC-21 — Usar o balcão virtual
**Actor:** Utilizador autenticado e agente de atendimento.
**Fluxo principal:**
1. O utilizador abre um pedido no balcão.
2. O sistema cria um caso e associa o serviço.
3. O agente recebe o pedido.
4. São trocadas mensagens e solicitados documentos.
5. O caso passa por estados e revisões.
6. Eventos, notificações e pagamentos são registados.
7. O caso é resolvido ou encerrado.
**Dados:** `desk_service_versions`, `desk_cases`, `desk_requests`,
`desk_documents`, `desk_events`, `desk_revisions`, `desk_messages`,
`desk_notifications`, `desk_institutional_links`, `desk_payments`.
## 24. UC-22 — Usar inteligência artificial com evidência
**Actor:** Gestor autorizado.
**Fluxo principal:**
1. O gestor activa a capacidade em `ai_controls`.
2. Regista uma fonte ou documento.
3. O sistema cria chunks da fonte.
4. O gestor inicia uma execução de IA.
5. O sistema grava `ai_runs`.
6. O resultado pode ser revisto ou receber feedback.
7. A aplicação mantém a fonte que suporta a resposta.
**Dados:** `ai_controls`, `ai_sources`, `ai_chunks`, `ai_runs`, `ai_feedback`,
`documents`, `users`.
## 25. UC-23 — Sincronizar com sistemas externos
**Actor:** Sistema externo ou processo de integração.
**Fluxo principal:**
1. A aplicação gera um evento em `outbox_events`.
2. Um worker publica o evento.
3. O sistema externo responde ou envia evento para `inbox_events`.
4. A aplicação cria `sync_runs`.
5. Falhas ou divergências são registadas em `reconciliation_issues`.
6. O processo pode ser repetido de forma idempotente.
## 26. UC-24 — Auditoria e controlo de alterações
**Actor:** Sistema, gestor ou auditor.
**Fluxo principal:**
1. Uma operação protegida é executada.
2. O sistema identifica actor, entidade, acção e correlação.
3. Regista evento de auditoria.
4. Alterações de entidades versionadas geram revisão/snapshot.
5. O auditor consulta o histórico sem alterar o registo original.
**Dados:** `audit_events`, `simulation_audit_events`, `investment_zone_revisions`,
`existing_business_revisions`, `investment_submission_revisions`,
`desk_revisions`, `outbox_events`.
## 27. Regras transversais da aplicação
- Dados privados exigem autenticação e autorização.
- Divulgação pública depende do estado de publicação e autorização registada.
- Documentos originais devem ser preservados.
- Hash, versão e proveniência devem permanecer associados ao documento.
- Relações jurídicas ou territoriais não validadas ficam pendentes.
- Rascunho não é submissão aprovada.
- Importação não é publicação.
- Publicação de documento não é activação de regra fiscal.
- Cálculos fiscais devem usar fonte e parâmetro identificados.
- Histórico e auditoria não devem ser apagados por rollback destrutivo.
- Operações de migração e testes devem usar base QA isolada.
## 28. Mapa resumido dos processos
```text
Visitante
-> consulta catálogo
-> interesse / apoio / simulação
Investidor
-> conta
-> favoritos / dossier
-> submissão
-> documentos
-> mensagens
Gestor
-> interesse
-> lead
-> tarefas
-> oportunidade CRM
-> decisão
Gestor territorial
-> projecto / cluster / corredor
-> zona
-> geometria
-> fonte legal
-> validação / publicação
Gestor fiscal
-> regra
-> revisão
-> aprovação
-> publicação
-> parâmetro usado pelo simulador
Parceiros
-> parceria
-> candidatura
-> sala
-> mensagens / documentos / marcos
```
@@ -4,6 +4,10 @@ Data: 2026-10-03. Âmbito: prompt master recebido em `Pasted text.txt`, document
## Estado inicial e ambiente
**Orientação de publicação e percurso do lote, 23/10/2026:** as 34 oportunidades de Agronegócio ficam publicáveis sem relações obrigatórias com corredores/clusters/projectos. Lacuna encontrada: a comparação múltipla existente não era transportada ao dossier. Correcção aditiva no contexto cifrado existente, com escolhas identificadas e localização por ficha, confirmação de alternativa, requisitos/fontes/documentos/entidades por contexto, reutilização no simulador e conservação de resultados/histórico. Sete testes do contexto (132 asserções) e um do lote pela sessão HTTP (15) passaram. Publicação QA efectuada; instalação no destino separa código, cadastro privado e divulgação autenticada. Ver [orientação e guia no manual](unification/traceability-and-manual.md). Não altera Turismo de Inhambane nem concede benefícios por localização. Lacunas de modalidade/dimensão/acto concreto permanecem explícitas, sem nova revalidação de fontes oficiais aceites.
**Correcção de âmbito, 23/10/2026 — agronegócio e percurso existente:** a lista informativa instalada não encerrava o cadastro. Preparadas e efectivamente cadastradas 34 oportunidades privadas em QA, com as seis designações documentais intactas, sector, fonte/hash e lacunas explícitas. Reutilizada a candidata de divulgação promocional `9d3bfa89` no fluxo necessário, sem alterações independentes de administração de contas nem novos papéis. Equipa edita/recupera e divulga/actualiza/retira pela interface; público acede à ficha individual, manifestação e roteiro com referência confirmada no mesmo dossier. Três testes específicos, 553 asserções; tipos/build concluídos; tabelas/linhas preexistentes comparadas e preservadas. As divulgações dos testes foram revertidas; as 34 fichas permanecem privadas. Sem alteração da homologação nesta correcção. Carregamento inicial técnico QA já executado; não é dependência das operações correntes. Guia, limites e recibo na [secção de correcção do manual](unification/traceability-and-manual.md#correcção-de-âmbito--34-oportunidades-cadastradas-e-roteiro-em-qa-23102026). Não encerra viabilidade, implantação, financiamento ou execução das oportunidades.
| Item | Evidência observada |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Branch / commit | `main` / `4f35313d06ccd597a4ebf9ed043964d898c95813` |
@@ -315,3 +319,37 @@ Situação comum observada: `publication_status=draft`, `validation_status=valid
- Verificação: `php artisan test tests/Feature/PortalServicesTest.php` — 17 testes/497 asserções aprovados em QA; cobrem publicação autorizada com pesquisa textual e filtros combinados, ficha, download, associação ao módulo e links de requisitos com estados confirmados/pendentes. `npm.cmd run build` concluído. O build alerta sobre pacote opcional `fontaine` e chunk `services` acima de 500 KB; sem erro de compilação. Não houve browser autenticado disponível, portanto não se declara publicação, pesquisa ou download públicos concluídos.
- A autorização documental não foi aplicada a zonas, parques, negócios privados ou oportunidades de demonstração. A regra de publicação de parque não exige área confirmada; divergência de área, isoladamente, não é um gate técnico. Se a ficha avançar por decisão própria, deve omitir o total disputado ou mostrar os valores reportados com fonte/escopo, sem alegar disponibilidade. Beluluane, Munhava e Topuito requerem revisão por registo para fonte/localização pública e para retirar ou qualificar a área; Topuito só tem província associada no mestre geográfico, pelo que não se pode anunciar uma associação administrativa a Larde enquanto essa referência não existir. ZEE/ZFI continuam dependentes de acto oficial estruturado e validado, regime e relações territoriais apropriadas; uma área sem confirmação não substitui essas evidências.
- Proposta para as seis oportunidades de demonstração já públicas: preservar os registos e IDs, adicionar origem/tipo `demo` e identificá-los visivelmente como **Exemplo de demonstração**, separar a pesquisa de exemplos da pesquisa de oportunidades reais e excluir os exemplos dos resultados reais por defeito. Não os converter nem retirar em massa; cada registo real exigirá origem e proponente documentados. Nenhuma alteração foi feita nesta execução.
### Referência da entrada única de gestão — QA, 22/10/2026
Nos requisitos existentes `MASTER-9f6dd1d9-0001`, `MASTER-9f6dd1d9-0003` e `MASTER-02f44baf-0002`, acrescentar como evidência delimitada a secção «Entrada única de gestão — preparação em QA, 22/10/2026» do manual de rastreabilidade e `tests/Feature/ContentManagementEntryTest.php`: quatro testes/99 asserções, ClamAV real e reversão transaccional em QA. Origem do objectivo e das regras operacionais: decisão expressa da Tempo; navegação, filtros e recuperação do upload são escolhas de implementação. Reutilizados biblioteca, armazenamento, perfis e histórico. Não altera os critérios de aceitação nem fecha os requisitos integrais: integração/contas reais e categorias não demonstradas conservam o seu estado. Geometrias permanecem separadas.
AUD-GEO-02/03: Recuperação da demarcação territorial — QA, 22/10/2026. 408/581 limites recuperados em QA; cadeia territorial guardada/reaberta no portal. Homologacao ainda sem reposicao; consultar a nova secao no manual existente. Mantidas 173 correspondencias pendentes e ausencia de geometrias proprias das localidades. Origem: pedido expresso da Tempo de recuperar funcionalidade; enquadramento e mensagens sao escolhas tecnicas. Nao encerra o criterio de reposicao no destino.
AUD-GEO-02/03 - 22/10/2026: Demarcação territorial reposta na homologação — 22/10/2026. 408 limites (11/133/264) importados pela conta real/MFA, com tres recibos auditados; backup/restauro de 171 tabelas conferido, 333 ficheiros verificados. Consulta do mapa demonstrada na oportunidade real #30, sem a alterar. Acessos previstos e originais privados preservados. Reposicao no destino concluida; 26+147 correspondencias e ausencia de poligonos proprios de localidades mantidas como lacunas reais. Ver fecho e guia no manual existente; sem repetir provas QA aceites.
### Incremento Concursos e anúncios — QA, 22/10/2026
Instalação autorizada e concluída na homologação em 22/10/2026: integração funcional `f4d09709`, schema selectivo/contrato/activação conferidos após backup e restauro isolado de 171 tabelas. Gestão real com MFA: cadastro, recuperação e prévia de rascunho privado auditado; catálogo público 200, zero anúncios publicados. Preservados dados/permissões/originais e geometrias. Evidência e links finais em «Instalação concluída na homologação» no manual e `unification/procurement-installation-receipt-20261022.json`. Provas QA aceites reutilizadas. Este fecho substitui o estado de proposta pendente de instalação abaixo, sem declarar aceite pela Tempo o guia não conferido nem expandir o âmbito para recepção de propostas.
Proposta de entrega da candidata `1bcf34c0`: ver «Proposta de instalação compatível com deploy automático» no manual existente. Identificados três novos cadastros e o gate real de contrato de migrations do pipeline. Sequência proposta: revisão → coordenação do deploy → backup/restauro isolado → migration selectiva autenticada com flag desligada → contrato → integração/build/promoção → activação → conferência do delta no destino. Reversão conserva tabelas, actos, auditoria e originais posteriores; não restaura backup antigo sobre dados novos. Provas QA reutilizadas; nenhuma integração, migration remota, activação ou publicação real executada nesta passagem. A Tempo ainda não conferiu links locais/guia; não registar aceitação nem entrega no destino por essa razão.
Nos requisitos existentes de gestão/publicação `MASTER-9f6dd1d9-0001`, `MASTER-9f6dd1d9-0003` e `MASTER-02f44baf-0002`, registar o recorte solicitado pela Tempo: procedimentos com regime/edição/fontes, lotes, rascunhos recuperáveis, prévia, actos versionados, catálogo/ficha/anexos anónimos, filtros, impressão e acompanhamento por conta simples. Evidência: secção «Concursos e anúncios — implementação em QA» do manual existente e `tests/Feature/ProcurementTest.php`, com passagem adicional no navegador dos três regimes, prorrogação, cancelamento, download e acompanhamento. Reutilizadas autorizações e biblioteca; não foram criados papéis ou aprovadores. Origem funcional: pedido expresso da Tempo; flag, prévia assinada e controlo de versão são escolhas técnicas. Entrega delimitada a QA: integração em main, migração/activação no destino e anúncios reais seguem o processo existente. Não fecha requisitos integrais de contratação/recepção de propostas, nem declara parametrizado todo o articulado legal. Geometrias permanecem no recorte separado já entregue.
### Preservação do concluído e reposição — 23/10/2026
Origem: pedido expresso da Tempo de inventariar o concluído e repor funcionalidades desaparecidas antes de outra ronda de testes/validações. Inventariados os mesmos 43 componentes do manual e os 17 percursos da matriz executável existente; os componentes de interface referenciados estão presentes. Estados históricos foram reconciliados com entregas posteriores, sem revogar aceitações por falta de nova prova. Não se criou nova matriz/backlog nem se promoveu uma funcionalidade parcial a concluída.
Perda confirmada: carteira e projectos âncora. Os 29 dossiers existiam em homologação, todos em rascunho, apesar de os exemplares locais idênticos estarem publicados com aprovação documental do proprietário de 26/09. Conteúdo, fonte e título conferidos por identidade/código e hashes nos 29; o commit `e65cb269` de 06/10 retirou o caminho de publicação com essa aprovação do importador. Isso explica a diferença do processo de instalação; não prova quando a visibilidade desapareceu de um endereço específico. Reposta a visibilidade dos 15 projectos, 8 clusters e 6 corredores em QA e homologação, com versões e 29 eventos de reposição; sem importação, reescrita de conteúdos, alteração dos mestres ou novas aprovações/papéis.
Backup/restauro fresco de 174 tabelas antes da escrita activa; depois da reposição, 171 tabelas inalteradas. Diferenças limitadas a dossiers, auditoria e sessões. Biblioteca, 20 artigos públicos, 28 privados, geometrias e permissões preservados. Conferência pública delimitada: carteira 29, projectos 15, clusters 8, corredores 6. Sem suite geral de testes nesta passagem. Evidência, limites e regra de preservação em `unification/preservation-inventory-20261023.json`, secção «Preservação do concluído — inventário e reposição» do manual e instrução permanente de 23/10 em `AGENTS.md`.
### Acervo legado — correspondência e preparação QA, 22/10/2026
Fecho do delta privado na homologação: candidata `fb4e7d89` integrada em `65eddf6b`; backup/restauro isolado, transporte privado e cadastro autenticado dos 28 concluídos, sem publicação. Pesquisa/editor/prévia/recuperação conferidos para os 28; gravação/recuperação visual da revisão 3 pela conta real no recurso 32. Originais, registos anteriores e permissões preservados. Recibo `unification/legacy-editorial-installation-receipt-20261022.json` e secção «Legado — cadastro privado concluído na homologação» do manual. Publicação e revisão editorial efectiva cabem à equipa pela interface; dois originais continuam incompletos. Não fecha nem diagnostica a falha separada da carteira/projectos âncora, nem constitui aceitação do commit/pacote pela Tempo.
Actualização do mesmo requisito: os 28 rascunhos têm edição, leitura privada, prévia sem guardar, recuperação e publicação/retirada pela interface em QA, preservando originais, origem e revisões. Seis testes/1.000 asserções, TypeScript e build passaram; publicação positiva apenas em transacções revertidas. Pacote privado v2 preparado com 28 originais e proveniência; não transportado nem publicado na homologação. Guia, permissões existentes, lacunas de imagens, dependências da entrega e reversão na secção «Legado — edição e leitura dos 28 rascunhos em QA» do manual. Pedido funcional expresso da Tempo; hash de âmbito, prévia e controlo de versão são escolhas técnicas, sem novo papel/aprovador. Conserva o saldo de 20 públicos aceites e dois originais incompletos, sem repetir provas ou fechar a integração integral.
Referência do acervo legado, 22/10/2026: a lista única `v2/auditoria-portal/legado-correspondencia-integral.md` e o CSV existente reconciliam 50 identidades (20 publicados, 28 por integrar no destino, um vazio, um sem título). 20/20 páginas conferidas por HTTP anónimo/identidade/origem; cartões de Turismo observados no destino. Os 28 restantes avançaram para rascunhos internos QA, com ClamAV real e repetição idempotente, sem publicação. Recibo e limites no manual, secção «Legado — correspondência integral e avanço privado QA». As duas lacunas editoriais foram confirmadas no original. Não fecha a integração integral nem substitui revisão editorial por prova HTTP; nenhuma alteração activa nesta passagem.
@@ -0,0 +1,211 @@
# Levantamento funcional, entidades e eventos — InvestMoz
**Data do levantamento:** 9 de Outubro de 2026
**Âmbito:** aplicação Laravel/React presente neste repositório.
**Base de evidência:** README, rotas, controladores, modelos, migrations e documentação em `docs/investmoz/`. A existência de uma tabela, rota ou documento não prova que a função esteja activa em produção.
## 1. Resumo executivo
O projecto é uma plataforma de investimento com área pública e área autenticada. Abrange descoberta de oportunidades e negócios, manifestações de interesse, gestão de candidaturas e carteira, CRM local, geografia e zonas de investimento, simulador e governação fiscal, catálogo documental, educação, assistência de terra, parcerias, balcão virtual, IA com fontes e integrações preparadas.
O README apresenta a base inicial do portal (sectores, oportunidades e manifestações). O código actual e os documentos de domínio registam a extensão funcional posterior. Para o inventário detalhado dos fluxos, consultar [casos-de-uso-aplicacao-actual.md](casos-de-uso-aplicacao-actual.md), que identifica 24 casos de uso. Este documento complementa-o com uma visão de domínio, um diagrama resumido de relações e a frequência observável dos eventos.
## 2. Âmbito e método
Foi feito inventário dos caminhos acessíveis no repositório: **981 ficheiros** por `rg --files`, excluindo dependências geradas `vendor` e `node_modules`. Dentro da aplicação foram consultados, em particular, `routes/`, `app/Models/`, `app/Http/Controllers/`, `database/migrations/`, `database/seeders/` e `docs/investmoz/`. O número é uma contagem do inventário local nessa data, não um inventário de anexos externos ou de dados de produção.
Classificação usada:
- **Implementado no código:** rota, controlador, job, comando ou schema identificável.
- **Documentado/planeado:** descrito em documentação, sem prova suficiente de execução integrada.
- **Frequência de negócio desconhecida:** não há amostra de eventos de produção nem telemetria para calcular volume por hora/dia/mês.
- **Cadência técnica explícita:** periodicidade configurada no scheduler ou no mecanismo de fila.
## 3. Actores e sistemas
| Actor | Objectivo principal | Áreas usadas |
|---|---|---|
| Visitante | Consultar conteúdos públicos e iniciar pedidos | Portal, oportunidades, negócios, zonas, documentos, simulador |
| Investidor/utilizador | Manter perfil, favoritos, rascunhos, submissões, documentos e mensagens | Espaço do investidor, carteira, assistência, parcerias |
| Gestor de portal/CRM | Rever interesses, gerir leads, tarefas, catálogo e publicação | CRM local, gestão de portal |
| Gestor territorial | Manter projectos, clusters, corredores, relações, geografia e zonas | Catálogo territorial, GIS, zonas |
| Revisor documental | Conferir proveniência, versão, hash, autorização e análise antimalware | Biblioteca documental |
| Gestor fiscal | Rever fontes, parâmetros e versões das regras fiscais | Governação fiscal, simulador |
| Gestor de educação | Manter competências, necessidades, cursos, capacidades e propostas | Educação |
| Gestor/agente de assistência | Tratar pedidos de terra e pedidos do balcão virtual | Assistência de terra, balcão |
| Parceiro institucional | Participar em oportunidades/parcerias e colaborar em salas | Parcerias |
| Administrador | Gerir autenticação, segurança, configurações e permissões | Administração |
| Sistema externo/worker | Receber/enviar mensagens ou executar trabalho assíncrono | Outbox/inbox, filas, integrações |
Papéis são uma leitura funcional dos fluxos. A autorização efectiva depende das policies, middleware e permissões implementadas, não apenas desta tabela.
## 4. Catálogo de casos de uso
O documento [Casos de uso da aplicação actual](casos-de-uso-aplicacao-actual.md) detalha pré-condições, fluxos e dados para cada caso. Índice consolidado:
| ID | Caso de uso | Actor principal | Resultado |
|---|---|---|---|
| UC-01 | Consultar página inicial e serviços | Visitante | Entrada no serviço público autorizado |
| UC-02 | Pesquisar sectores e oportunidades | Visitante/investidor | Lista/detalhe e possível favorito/interesse |
| UC-03 | Consultar negócio existente | Visitante/investidor | Consulta da oferta divulgável |
| UC-04 | Criar conta, autenticar e recuperar acesso | Utilizador | Sessão autenticada/verificada |
| UC-05 | Manifestar interesse numa oportunidade | Visitante/investidor | Pedido recebido para tratamento |
| UC-06 | Manifestar interesse num negócio existente | Investidor | Pedido ligado ao negócio e consentimento |
| UC-07 | Guardar oportunidade e manter espaço do investidor | Utilizador | Favorito, dossier ou item na carteira |
| UC-08 | Criar e manter submissão de investimento | Investidor | Submissão em rascunho com histórico |
| UC-09 | Submeter candidatura | Investidor | Entrada em fila de tratamento |
| UC-10 | Gerir negócio existente | Proprietário/gestor | Registo revisto e eventualmente publicável |
| UC-11 | Tratar interesses no CRM | Gestor | Qualificação, lead, tarefa e seguimento |
| UC-12 | Gerir catálogo territorial | Gestor territorial | Projectos, clusters, corredores e relações |
| UC-13 | Gerir zonas de investimento | Gestor territorial/revisor | Zona validada, publicada ou suspensa |
| UC-14 | Carregar, verificar e publicar documento | Revisor documental | Documento preservado com estados independentes |
| UC-15 | Criar/retomar rascunho de simulação | Visitante/utilizador | Rascunho recuperável, sem cálculo implícito |
| UC-16 | Executar simulação | Visitante/utilizador | Resultado, obrigações, fontes e relatório |
| UC-17 | Gerir regra fiscal | Gestor fiscal | Versão de regra passa por revisão/aprovação |
| UC-18 | Gerir educação e competências | Gestor de educação | Catálogo, análise de lacunas e proposta |
| UC-19 | Abrir/tratar assistência de terra | Investidor/gestor | Caso acompanhado até encaminhamento/fecho |
| UC-20 | Criar e gerir parceria | Investidor/parceiro | Interesse, sala, mensagens, documentos e marcos |
| UC-21 | Usar balcão virtual | Utilizador/agente | Pedido de serviço acompanhado até resolução |
| UC-22 | Usar IA com evidência | Gestor autorizado | Execução rastreável ligada a fontes e feedback |
| UC-23 | Sincronizar sistemas externos | Worker/sistema externo | Eventos recebidos/enviados e divergências registadas |
| UC-24 | Auditar alterações | Sistema/gestor/auditor | Histórico e correlação para operações relevantes |
## 5. Diagrama de entidades e relações
Diagrama lógico resumido por domínios. As cardinalidades representam a estrutura documentada; algumas relações polimórficas, opcionais ou em tabelas de junção estão simplificadas. Para o ERD de schema detalhado, consultar [erd.mmd](erd.mmd), [data-dictionary.csv](data-dictionary.csv) e [table-catalog.csv](table-catalog.csv).
```mermaid
erDiagram
USER ||--o{ INVESTOR : possui
USER ||--o{ SUBMISSION : cria
USER ||--o{ SIMULATION : executa
USER ||--o{ AUDIT_EVENT : origina
SECTOR ||--o{ OPPORTUNITY : classifica
OPPORTUNITY ||--o{ INTEREST_REQUEST : recebe
EXISTING_BUSINESS ||--o{ INTEREST_REQUEST : recebe
INVESTOR ||--o{ INTEREST_REQUEST : manifesta
INTEREST_REQUEST ||--o| LEAD : converte
LEAD ||--o{ CRM_TASK : acompanha
OPPORTUNITY ||--o{ SUBMISSION : referencia
SUBMISSION ||--o{ SUBMISSION_REVISION : versiona
SUBMISSION ||--o{ SUBMISSION_EVENT : regista
SUBMISSION ||--o{ DOCUMENT_LINK : anexa
PROJECT }o--o{ ECONOMIC_CLUSTER : associa
PROJECT }o--o{ DEVELOPMENT_CORRIDOR : associa
PROJECT ||--o{ INVESTMENT_GAP : identifica
INVESTMENT_ZONE ||--o{ ZONE_REVISION : versiona
INVESTMENT_ZONE ||--o{ ZONE_RELATION : liga
INVESTMENT_ZONE ||--o{ ZONE_ATTACHMENT : documenta
DOCUMENT ||--o{ DOCUMENT_VERSION : versiona
DOCUMENT ||--o{ MALWARE_SCAN : analisa
DOCUMENT }o--o{ PORTAL_RESOURCE : disponibiliza
SIMULATION ||--o{ SIMULATION_SCENARIO : contém
SIMULATION_SCENARIO ||--o{ SIMULATION_RESULT : produz
SIMULATION_RESULT ||--o{ SIMULATION_OBLIGATION : discrimina
SIMULATION_RESULT ||--o{ RESULT_SOURCE : fundamenta
TAX_RULE_VERSION }o--o{ LEGAL_SOURCE : fundamenta
PROJECT ||--o{ TRAINING_NEED : requer
SKILL }o--o{ OCCUPATION : relacionada
TRAINING_PROVIDER ||--o{ COURSE : oferece
USER ||--o{ LAND_ASSISTANCE_CASE : requer
PARTNERSHIP ||--o{ PARTNERSHIP_INTEREST : recebe
PARTNERSHIP ||--o| PARTNERSHIP_ROOM : abre
PARTNERSHIP_ROOM ||--o{ PARTNERSHIP_MESSAGE : contém
DESK_CASE ||--o{ DESK_EVENT : regista
AI_SOURCE ||--o{ AI_CHUNK : divide
AI_SOURCE ||--o{ AI_RUN : suporta
OUTBOX_EVENT }o..|| SYNC_RUN : processa
SYNC_RUN ||--o{ RECONCILIATION_ISSUE : detecta
```
### 5.1 Domínios e entidades nucleares
| Domínio | Entidades nucleares | Relações principais |
|---|---|---|
| Identidade e acesso | `users`, `passkeys`, sessões, organizações, memberships | Utilizador possui perfil e inicia operações protegidas |
| Catálogo público | `sectors`, `opportunities`, catálogos de referência, recursos do portal | Sector classifica oportunidades; publicação controla exposição |
| Negócios existentes | `existing_businesses`, objectos, locais, revisões, mandatos | Negócio tem objectos/locais/documentos e workflow próprio |
| Investidor e CRM | `investors`, `interest_requests`, `leads`, `contacts`, oportunidades CRM, tarefas | Interesse pode converter-se em lead e gerar tarefas/actividades |
| Carteira e submissões | dossiers, `investment_submissions`, necessidades, revisões, eventos, mensagens, anexos | Submissão pertence a utilizador e liga-se a oportunidade/negócio |
| Território e GIS | `projects`, `economic_clusters`, `development_corridors`, `geo_locations`, `value_chains`, `investment_gaps` | Relações muitos-para-muitos documentam associação territorial/económica |
| Zonas de investimento | `investment_zones`, fontes legais, geometrias, relações, anexos, revisões | Zona tem localização, evidência, relações e estado de validação/publicação |
| Documentos | `documents`, versões, scans, associações por domínio, recursos do portal | Documento pode ser associado a várias entidades; disponibilidade, estado documental e uso em cálculo são independentes |
| Simulação e fiscalidade | rascunhos, `simulations`, cenários, resultados, obrigações, fontes, regras versionadas | Execução usa versões/parâmetros elegíveis e preserva as fontes do resultado |
| Educação | skills, occupations, providers, courses, training needs/capacity, propostas | Projectos expressam necessidades relacionadas com competências e formação |
| Serviços e colaboração | casos de terra, balcão virtual, parcerias, salas, mensagens, documentos, marcos | Casos e salas mantêm eventos e participantes próprios |
| IA e governação | controlos, fontes/chunks, execuções, feedback, auditoria | Execução de IA deve poder ser rastreada às fontes e ao actor |
| Integrações | `outbox_events`, `inbox_events`, `sync_runs`, reconciliações | Eventos são idempotentes/correlacionáveis; desenho de schema não prova integração activa |
## 6. Eventos e frequência
### 6.1 Frequência explicitamente configurada
| Evento/processo | Gatilho | Frequência/cadência no código | Natureza e observação |
|---|---|---|---|
| Escalar tarefas vencidas (`investmoz:escalate-overdue-tasks`) | Scheduler Laravel | **A cada 15 minutos**, com `withoutOverlapping()` | Única periodicidade de scheduler encontrada em `routes/console.php`; execução real requer scheduler/cron activo |
| Executar simulação (`RunSimulation`) | Pedido de cálculo aceite | Por submissão, via fila, `afterCommit` | Assíncrono; frequência depende da procura e de workers activos |
| Analisar documento (`ScanDocumentForMalware`) | Upload/solicitação de scan em fluxos suportados | Por documento, via fila, `afterCommit`; job único por identidade configurada | Cadência sob procura; requer worker e scanner configurados. Upload/publicação não confirma scan limpo |
| Comparar compatibilidade MNE (`CompareMneBusiness`) | Alteração de negócio que agenda comparação | Por alteração aplicável, em fila específica e `afterCommit` | Trabalho assíncrono/sombra; volume real desconhecido |
| Notificar recepção de interesse (`NotifyInterestReceived`) | Disparo esperado após recepção de interesse | Job existe, mas não foi localizada chamada a `dispatch()` no código consultado; frequência de execução não confirmada | Tratar como capacidade prevista até localizar/activar o disparo; entrega efectiva também depende de configuração e worker |
| Auditoria, revisões e eventos de domínio | Acção de utilizador/sistema | No momento da operação que os gera | Orientado a evento; não há taxa histórica neste levantamento |
| Outbox/inbox e sincronização externa | Evento de domínio ou mensagem externa | Sem periodicidade operacional confirmada neste código/documentação consultada | Estrutura preparada; documentação aponta sincronização externa real como pendente/não activada |
### 6.2 Frequência por categoria de actividade
| Categoria | Frequência funcional esperada | O que é possível afirmar |
|---|---|---|
| Leituras públicas (home, catálogo, oportunidade, negócio, mapas/referências) | Sob procura do visitante | Há endpoints/páginas; sem logs ou analytics não se quantificam consultas |
| Escritas de investidor (interesses, favoritos, submissões, mensagens, perfil) | Sob acção do utilizador | São iniciadas por pedido explícito; não há volume observado disponível |
| Gestão (revisão, publicação, CRM, regras, zonas, catálogo) | Sob trabalho de gestores autorizados | Fluxos e protecções documentados; frequência por papel não medida |
| Simulação e rascunhos | Sob utilização do simulador; rascunhos podem ser actualizados/retomados | Rota e jobs encontrados; não assumir execução periódica nem cálculo fiscal por guardar rascunho |
| Uploads e análise documental | Sob envio/reanálise de documento | Job de scan é disparado por fluxos suportados; scan e publicação são estados distintos |
| Auditoria | Sob operações auditadas | A frequência acompanha as operações instrumentadas; cobertura total precisa de confirmação por fluxo |
| Regras fiscais e fontes | Sob processo de revisão/aprovação e alteração normativa | Não há calendário fixo; fonte publicada não activa automaticamente cálculo |
| Integrações externas | Não determinável/activação pendente | Tabelas de integração e documentação não são prova de entrega ou sincronização real |
### 6.3 Frequência observada versus frequência de execução
Não foram disponibilizados logs, contagens de `audit_events`, filas, analytics, pedidos HTTP ou exportação da base activa. Por isso, **não é possível apresentar eventos por hora/dia/mês, sazonalidade, taxa de falhas ou utilizadores activos**. A cadência de 15 minutos é apenas o intervalo configurado para o comando agendado. Para apurar frequência real, recolher métricas autorizadas por tipo de evento, intervalo temporal e resultado, sem expor dados pessoais; documentar período da amostra, filtros, retenção e origem. Não inferir métricas a partir de seeders, testes, screenshots ou schema.
## 7. Sequência de alto nível
```text
Visita pública
-> pesquisa catálogo/oportunidades/negócios/território
-> interesse, candidatura, assistência ou simulação
-> autenticação e consentimento quando exigidos
-> gravação do pedido/rascunho e histórico
-> trabalho assíncrono quando aplicável (scan, cálculo, notificação)
-> revisão por responsável autorizado
-> decisão, comunicação ou publicação com autorização
-> auditoria e eventual integração externa homologada
```
## 8. Regras transversais que afectam os fluxos
- Disponibilidade para consulta, situação jurídica/documental e uso em regra/cálculo são estados independentes.
- Importar ou publicar documento não activa regra nem cálculo fiscal.
- Relações sem citação/evidência verificada devem ficar pendentes e identificadas.
- Guardar um rascunho não equivale a submeter ou aprovar.
- A divulgação obedece à autorização registada e ao seu âmbito.
- Documento original, proveniência, identificador, edição/data, hash e histórico devem ser preservados; não marcar autenticidade, vigência, direitos ou scan como confirmados sem evidência.
- Integração desenhada (outbox/inbox) não equivale a sincronização activa.
- Alterações a dados activos exigem confirmação de backup/restauro e acção autenticada necessária; testes/migrações devem usar exclusivamente base QA isolada, conforme `AGENTS.md`.
## 9. Limitações e pontos para validação
1. A cobertura implementada de cada UC varia; alguns documentos descrevem trabalho parcial, proposto ou bloqueado. Consultar `docs/investmoz/validation.md`, `completion-audit-2026-10-04.md`, `delivery-gaps.md` e os README de cada domínio antes de declarar operação concluída.
2. O ERD resumido não substitui constraints/FKs, cardinalidades exactas, escopos de acesso ou estados enumerados no schema e OpenAPI.
3. A frequência real de utilização e dos eventos de negócio permanece **indeterminada** sem telemetria operacional.
4. Execução do scheduler, workers, ClamAV, notificações, SuiteCRM e restantes conectores depende de configuração e operação externa verificável.
5. O inventário desta tarefa é do repositório, não dos ficheiros/anexos em armazenamento externo nem da base de dados de produção.
## 10. Fontes locais principais
- [README do projecto](../../README.md)
- [Casos de uso detalhados](casos-de-uso-aplicacao-actual.md)
- [ERD do schema reconciliado](erd.mmd)
- [Arquitectura e migrations](architecture-and-migrations.md)
- [Validação](validation.md) e [auditoria de conclusão](completion-audit-2026-10-04.md)
- Rotas: `routes/web.php`, `routes/api.php`, `routes/investment.php`, `routes/partnerships.php`, `routes/land-assistance.php`, `routes/investment-zones.php`, `routes/territorial-registry.php`
- Cadência: `routes/console.php`; trabalhos assíncronos em `app/Jobs/`
@@ -0,0 +1,710 @@
{
"date": "2026-10-23",
"decision": "Pedido expresso da Tempo: aplicar o anexo de identidade visual aos cinco cartões de Encontre o contexto do seu investimento.",
"instructionSource": {
"path": "C:\\Users\\jorge\\.codex\\attachments\\637b878f-1f58-490d-bd07-d5cd10ae2377\\Pasted text.txt",
"sha256": "fcc24dcce4b5df3f10e29add48052808d48ddcf674d052287854e11910e01d69"
},
"source": {
"type": "AI-generated photorealistic illustrative images",
"tool": "image_gen, built-in mode",
"skill": "C:/Users/jorge/.codex/skills/.system/imagegen/SKILL.md",
"externalPhotographsUsed": false,
"namedRealLocationsDepicted": false,
"note": "Cenas ilustrativas plausíveis, sem atribuição a instalações, pessoas, instituições ou projectos reais."
},
"analysisAndPlan": "docs/investmoz/public-portal-sections.md#fotografias-nos-cartões-de-contexto-do-investimento--23102026",
"filesChanged": [
"resources/js/pages/PortalInvestidor.tsx",
"resources/js/components/investment-context-cards.tsx",
"tools/portal-context-cards-qa.mjs",
"docs/investmoz/public-portal-sections.md",
"docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv",
"docs/investmoz/public-portal-context-images.json"
],
"images": [
{
"key": "clusters-agroindustria",
"prompt": "Use case: photorealistic-natural. Asset type: an individual navigation card photograph for a professional investment portal. Generate ONE clean, realistic editorial photograph in horizontal 16:9 composition. Natural daylight, realistic restrained colours, excellent photographic detail, balanced uncluttered framing, one clear subject readable at a small thumbnail size. Contemporary setting plausible in Mozambique in southern Africa. This is a generic illustrative scene, NOT a depiction or claim about any named real facility, project, landmark or institution. No text, lettering, titles, captions, logos, brands, watermarks, flags, official emblems, maps, diagrams, graphical overlays, UI elements, collages, artificial colour filters, futuristic architecture or advertising poster treatment. Realistic people, hands, equipment and perspective; dignified candid activity instead of posed stock photography. Subject: a modest, well maintained agro-processing packing facility linking commercial agriculture to local industry. Wide three-quarter view into an airy open-sided processing shed with pale walls and a few orderly wooden or reusable produce crates, stainless processing/sorting benches and an unobtrusive commercial agricultural field visible beyond. Two Black southern African workers in clean practical workwear sorting harvested vegetables naturally, faces not prominent. Show production and transformation together in ONE genuine-looking continuous scene, no montage. Clear simple geometry, warm-neutral sunlight, subtle vegetation.",
"source": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-f72e66b0-d788-4476-8b20-c39b22d5d6dc.png",
"original": "outputs/investment-context-20261023/originals/clusters-agroindustria.png",
"originalSha256": "75e04a694081ab757831fc9b37323949d09806e622c83d07588fc47c423c9033",
"originalDimensions": {
"width": 1672,
"height": 941
},
"derivatives": [
{
"file": "public/images/investment-context/clusters-agroindustria.webp",
"width": 960,
"height": 540,
"bytes": 116796,
"sha256": "12494b31ab551cffb79f81c7d1519c0c4a2d4236e7f129bec1ce32251b39d847"
},
{
"file": "public/images/investment-context/clusters-agroindustria-480.webp",
"width": 480,
"height": 270,
"bytes": 42060,
"sha256": "76e3152c99ed188f748542dcf0464787307dbdc1c6299e7264dfd1bd86ad75e3"
},
{
"file": "public/images/investment-context/clusters-agroindustria-fallback.webp",
"width": 240,
"height": 135,
"bytes": 9596,
"sha256": "9f668da50f7d64955b31940d239b420ac18dbb76a8120c8bedab3f2c04710288"
}
]
},
{
"key": "corredores-logisticos",
"prompt": "Use case: photorealistic-natural. Asset type: an individual navigation card photograph for a professional investment portal. Generate ONE clean, realistic editorial photograph in horizontal 16:9 composition. Natural daylight, realistic restrained colours, excellent photographic detail, balanced uncluttered framing, one clear subject readable at a small thumbnail size. Contemporary setting plausible in Mozambique in southern Africa. This is a generic illustrative scene, NOT a depiction or claim about any named real facility, project, landmark or institution. No text, lettering, titles, captions, logos, brands, watermarks, flags, official emblems, maps, diagrams, graphical overlays, UI elements, collages, artificial colour filters, futuristic architecture or advertising poster treatment. Realistic people, hands, equipment and perspective; dignified candid activity instead of posed stock photography. Subject: a commercial freight terminal serving a southern African logistics corridor. Elevated medium-wide viewpoint showing an orderly short line of unbranded freight containers, one realistic container-handling crane and a freight truck on a paved lane, with a calm strip of water and low coastal industrial vegetation in the distance. Infrastructure of credible modest contemporary scale, generic and unidentifiable; do not reproduce or name Maputo, Beira, Nacala or any real port. Spatially coherent containers, road and handling equipment; clean clear subject with no excessive machinery.",
"source": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-0363da23-f9cb-413b-bb64-024539085c4b.png",
"original": "outputs/investment-context-20261023/originals/corredores-logisticos-v2.png",
"originalSha256": "2151d1a59265c6f7151d18af6ef6ad78f9fc6e0cd58ac9cc5c64b50a25c5058e",
"originalDimensions": {
"width": 1672,
"height": 941
},
"derivatives": [
{
"file": "public/images/investment-context/corredores-logisticos-v2.webp",
"width": 960,
"height": 540,
"bytes": 85184,
"sha256": "1939c26df297588b6c3e5ea08b700d4cda5d1183cf3e5faed06a1ca060d2475e"
},
{
"file": "public/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 480,
"height": 270,
"bytes": 26222,
"sha256": "06dc0e7126cb2c13eb3f9a98f6ee58f5d64f9d07c28620975f744f263744fe15"
},
{
"file": "public/images/investment-context/corredores-logisticos-v2-fallback.webp",
"width": 240,
"height": 135,
"bytes": 5334,
"sha256": "f9fd6e6c3a2d4654df1a8fdb287e122f1889b5d318c104c4816ee393c793e242"
}
],
"selectedSource": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-c2cff7da-d602-4191-9aca-380918a09619.png",
"editPrompt": "Edit the provided photograph: REMOVE THE ENTIRE FOREGROUND TRUCK, including cab, wheels, long trailer and the red freight container on that trailer. The big truck at lower left/centre must be completely gone. Replace the removed truck's area with a continuous empty realistic paved road surface matching the surrounding pavement and road markings. Leave the stacked stationary freight containers along the quay, the water, the large port crane, the lifted container and workers. All remaining equipment and containers should have plain surfaces without readable lettering, logos, numbers or brands. No new truck, vehicle, symbol, text or overlay. Keep a single clean editorial photograph, natural lighting and horizontal 16:9 framing. Generic commercial port scene, not a named real facility."
},
{
"key": "projectos-ancora",
"prompt": "Use case: photorealistic-natural. Asset type: an individual navigation card photograph for a professional investment portal. Generate ONE clean, realistic editorial photograph in horizontal 16:9 composition. Natural daylight, realistic restrained colours, excellent photographic detail, balanced uncluttered framing, one clear subject readable at a small thumbnail size. Contemporary setting plausible in Mozambique in southern Africa. This is a generic illustrative scene, NOT a depiction or claim about any named real facility, project, landmark or institution. No text, lettering, titles, captions, logos, brands, watermarks, flags, official emblems, maps, diagrams, graphical overlays, UI elements, collages, artificial colour filters, futuristic architecture or advertising poster treatment. Realistic people, hands, equipment and perspective; dignified candid activity instead of posed stock photography. Subject: a small team of two Black southern African engineering professionals, one woman and one man, in realistic hard hats and restrained protective workwear, seen candidly from a three-quarter rear angle reviewing an unmarked folded technical plan at a utility-scale solar installation. Low rows of realistic blue solar panels and a simple low service building recede into a dry green southern African landscape. No readable drawing or numbers on the plan. Focus on credible engineering and investment project scale; clean composition, quiet professional atmosphere, no landmark or company identifiers.",
"source": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-19fcd882-542b-43c9-81e7-593fd75083f6.png",
"original": "outputs/investment-context-20261023/originals/projectos-ancora.png",
"originalSha256": "f23cf6e64307a280f3aa58741454a23480ac28fcae01a0810adf134b783a3a74",
"originalDimensions": {
"width": 1672,
"height": 941
},
"derivatives": [
{
"file": "public/images/investment-context/projectos-ancora.webp",
"width": 960,
"height": 540,
"bytes": 77114,
"sha256": "de0b59774246fee982761de59618b3e7707c3fe30f2b1bcbb6017d4dce53c4dc"
},
{
"file": "public/images/investment-context/projectos-ancora-480.webp",
"width": 480,
"height": 270,
"bytes": 29484,
"sha256": "268e405112ed341fce3c9792c3caa47053984100cd4a204453e31bbe5db6e890"
},
{
"file": "public/images/investment-context/projectos-ancora-fallback.webp",
"width": 240,
"height": 135,
"bytes": 6750,
"sha256": "db7e4b4382e09eb6b4f9f2a4603ccac4b4eacc53ba0f6776429e33dbf5131954"
}
]
},
{
"key": "educacao-formacao",
"prompt": "Use case: photorealistic-natural. Asset type: an individual navigation card photograph for a professional investment portal. Generate ONE clean, realistic editorial photograph in horizontal 16:9 composition. Natural daylight, realistic restrained colours, excellent photographic detail, balanced uncluttered framing, one clear subject readable at a small thumbnail size. Contemporary setting plausible in Mozambique in southern Africa. This is a generic illustrative scene, NOT a depiction or claim about any named real facility, project, landmark or institution. No text, lettering, titles, captions, logos, brands, watermarks, flags, official emblems, maps, diagrams, graphical overlays, UI elements, collages, artificial colour filters, futuristic architecture or advertising poster treatment. Realistic people, hands, equipment and perspective; dignified candid activity instead of posed stock photography. Subject: a candid technical training classroom with three Black southern African young adult students of different genders and one instructor working together at a practical electronics and computing workbench. Bright modest well maintained vocational classroom, natural window light, simple laboratory equipment and unbranded closed or angled laptops whose screens have no visible interface or text. Focus on two students engaged in a realistic small hands-on electronics exercise with normal anatomy and correct equipment proportions. Dignified contemporary clothing, natural concentration, professional skills and human capital investment, no posed smiles or stereotypes.",
"source": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-d21314d7-c726-41ad-862c-3c8b64df6b53.png",
"original": "outputs/investment-context-20261023/originals/educacao-formacao.png",
"originalSha256": "e59ec599e8c64ab84195d0cb37871d3102df178a13275806ca3b7f98e6bfa895",
"originalDimensions": {
"width": 1672,
"height": 941
},
"derivatives": [
{
"file": "public/images/investment-context/educacao-formacao.webp",
"width": 960,
"height": 540,
"bytes": 68224,
"sha256": "e3e5dbd292cac555a45bcbd94a0fa6b60e6b710debc0c827c68603f40bfb8a28"
},
{
"file": "public/images/investment-context/educacao-formacao-480.webp",
"width": 480,
"height": 270,
"bytes": 27384,
"sha256": "d3b743fbba7a8cb40e45999ee8c345cbfc0332cf85f5c5c2d6a5cf1240a395e4"
},
{
"file": "public/images/investment-context/educacao-formacao-fallback.webp",
"width": 240,
"height": 135,
"bytes": 7156,
"sha256": "0b2a12898043c57cf4354b54ebcba8adec8d41b3886bc574584ccc12af7b1e4c"
}
]
},
{
"key": "documentos-formularios",
"prompt": "Use case: photorealistic-natural. Asset type: an individual navigation card photograph for a professional investment portal. Generate ONE clean, realistic editorial photograph in horizontal 16:9 composition. Natural daylight, realistic restrained colours, excellent photographic detail, balanced uncluttered framing, one clear subject readable at a small thumbnail size. Contemporary setting plausible in Mozambique in southern Africa. This is a generic illustrative scene, NOT a depiction or claim about any named real facility, project, landmark or institution. No text, lettering, titles, captions, logos, brands, watermarks, flags, official emblems, maps, diagrams, graphical overlays, UI elements, collages, artificial colour filters, futuristic architecture or advertising poster treatment. Realistic people, hands, equipment and perspective; dignified candid activity instead of posed stock photography. Subject: a close-medium editorial photograph of a tidy modern administrative office desk where a Black southern African professional in a plain light blue shirt is naturally reviewing organised business documentation. Frame hands, desk and partial torso, no prominent portrait. Neat neutral folders, a few sheets with only faint out-of-focus non-readable printing, a pen and an unbranded laptop angled away with no visible UI. Soft window light, white and pale neutral surfaces with subtle blue accents. Absolutely no readable personal data, invented official seals or highlighted fake text; realistic fingers and documents, calm minimal composition.",
"source": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-f1c8e22e-6885-4578-ac7d-02692c5cc071.png",
"original": "outputs/investment-context-20261023/originals/documentos-formularios.png",
"originalSha256": "cfbd38a9cc9b9acf35f38220f5bd9c1ab1feafe411bc1d4bbdb3658de557e052",
"originalDimensions": {
"width": 1672,
"height": 941
},
"derivatives": [
{
"file": "public/images/investment-context/documentos-formularios.webp",
"width": 960,
"height": 540,
"bytes": 43002,
"sha256": "70ddea3256e6a8d981b1fae5ee7b4de6c910d757137b79a59306c268c278402f"
},
{
"file": "public/images/investment-context/documentos-formularios-480.webp",
"width": 480,
"height": 270,
"bytes": 17996,
"sha256": "436865c948acbb7b6e9e482bef8f5bd626c6960f2f155608e40dfcd0e76b84cb"
},
{
"file": "public/images/investment-context/documentos-formularios-fallback.webp",
"width": 240,
"height": 135,
"bytes": 4958,
"sha256": "fad6a6ab825a82ec2541d2e0c6de9d85486e4bbca159b912f87bec9cc0aeeba5"
}
]
}
],
"primaryImagesBytesTotal": 390320,
"responsive480ImagesBytesTotal": 143146,
"implementation": {
"gridColumns": [
1,
2,
4
],
"aspectRatio": "16:9",
"responsiveImages": true,
"lazyLoading": true,
"altLanguage": "pt",
"fallback": "Derivado pequeno da própria imagem; se também falhar, estado acessível Imagem indisponível, com dimensão reservada e link mantido.",
"globalCSSChanged": false,
"businessLogicChanged": false,
"databaseChanged": false,
"heroChanged": false
},
"visualReview": "Cinco composições individuais distintas, cores naturais e luz editorial; sem títulos/colagens ou mapas. A composição de Corredores foi revista para remover o camião com marca. Capturas em computador e telemóvel inspeccionadas.",
"verification": {
"browser": {
"results": [
{
"width": 320,
"dpr": 1,
"columns": 1,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria-480.webp",
"width": 320,
"height": 180
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 320,
"height": 180
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora-480.webp",
"width": 320,
"height": 180
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao-480.webp",
"width": 320,
"height": 180
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios-480.webp",
"width": 320,
"height": 180
}
],
"status": "passed"
},
{
"width": 390,
"dpr": 2,
"columns": 1,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria.webp",
"width": 389,
"height": 219
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2.webp",
"width": 389,
"height": 219
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora.webp",
"width": 389,
"height": 219
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao.webp",
"width": 389,
"height": 219
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios.webp",
"width": 389,
"height": 219
}
],
"status": "passed"
},
{
"width": 768,
"dpr": 1,
"columns": 2,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria-480.webp",
"width": 384,
"height": 216
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 384,
"height": 216
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora-480.webp",
"width": 384,
"height": 216
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao-480.webp",
"width": 384,
"height": 216
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios-480.webp",
"width": 384,
"height": 216
}
],
"status": "passed"
},
{
"width": 1024,
"dpr": 1,
"columns": 4,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria-480.webp",
"width": 256,
"height": 144
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 256,
"height": 144
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora-480.webp",
"width": 256,
"height": 144
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao-480.webp",
"width": 256,
"height": 144
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios-480.webp",
"width": 256,
"height": 144
}
],
"status": "passed"
},
{
"width": 1440,
"dpr": 2,
"columns": 4,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios.webp",
"width": 266,
"height": 149
}
],
"status": "passed"
},
{
"width": 1920,
"dpr": 1,
"columns": 4,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria-480.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora-480.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao-480.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios-480.webp",
"width": 266,
"height": 149
}
],
"status": "passed"
},
{
"fail": "primary",
"status": "passed"
},
{
"fail": "all",
"status": "passed"
}
],
"errors": [],
"navigation": [
"/clusters",
"/corredores",
"/projectos-ancora",
"/educacao",
"/documentos"
],
"databaseAccess": false
},
"script": "tools/portal-context-cards-qa.mjs",
"screenshots": [
"outputs/investment-context-20261023/cards-1024-dpr1.png",
"outputs/investment-context-20261023/cards-1440-dpr2.png",
"outputs/investment-context-20261023/cards-1920-dpr1.png",
"outputs/investment-context-20261023/cards-320-dpr1.png",
"outputs/investment-context-20261023/cards-390-dpr2.png",
"outputs/investment-context-20261023/cards-768-dpr1.png",
"outputs/investment-context-20261023/fallback-all.png",
"outputs/investment-context-20261023/fallback-primary.png"
],
"types": {
"command": "npm run types:check",
"exitCode": 0
},
"lint": {
"files": [
"resources/js/components/investment-context-cards.tsx",
"resources/js/pages/PortalInvestidor.tsx",
"tools/portal-context-cards-qa.mjs"
],
"exitCode": 0
},
"build": {
"command": "npm run build",
"exitCode": 0,
"pageAsset": "assets/PortalInvestidor-fh5VZanx.js",
"log": "outputs/investment-context-20261023/build-local.log"
}
},
"scope": "Aplicado no código e pacote locais. API e navegação simuladas na verificação visual; sem dados activos ou publicação externa.",
"matrixReferences": [
"MASTER-efd550fd-0104",
"MASTER-55050f60-0001"
],
"acceptanceLimit": "Prova restrita à apresentação e navegação dos cinco cartões; não encerra critérios funcionais mais amplos da matriz.",
"layoutRevision": {
"date": "2026-10-23",
"decision": "Pedido expresso da Tempo: título a vermelho e menos espaço em branco dentro dos cartões.",
"changes": "Título no vermelho #ed1c24; removidas alturas mínimas de título e descrição; preenchimento 20→16 px, intervalo 8→6 px e entrelinhas 28/24→24/20 px. Mantidos imagens, conteúdos, links e alinhamento por linha da grelha.",
"browser": {
"results": [
{
"width": 320,
"dpr": 1,
"columns": 1,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria-480.webp",
"width": 320,
"height": 180
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 320,
"height": 180
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora-480.webp",
"width": 320,
"height": 180
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao-480.webp",
"width": 320,
"height": 180
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios-480.webp",
"width": 320,
"height": 180
}
],
"status": "passed"
},
{
"width": 390,
"dpr": 2,
"columns": 1,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria.webp",
"width": 389,
"height": 219
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2.webp",
"width": 389,
"height": 219
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora.webp",
"width": 389,
"height": 219
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao.webp",
"width": 389,
"height": 219
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios.webp",
"width": 389,
"height": 219
}
],
"status": "passed"
},
{
"width": 768,
"dpr": 1,
"columns": 2,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria-480.webp",
"width": 384,
"height": 216
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 384,
"height": 216
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora-480.webp",
"width": 384,
"height": 216
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao-480.webp",
"width": 384,
"height": 216
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios-480.webp",
"width": 384,
"height": 216
}
],
"status": "passed"
},
{
"width": 1024,
"dpr": 1,
"columns": 4,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria-480.webp",
"width": 256,
"height": 144
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 256,
"height": 144
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora-480.webp",
"width": 256,
"height": 144
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao-480.webp",
"width": 256,
"height": 144
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios-480.webp",
"width": 256,
"height": 144
}
],
"status": "passed"
},
{
"width": 1440,
"dpr": 2,
"columns": 4,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios.webp",
"width": 266,
"height": 149
}
],
"status": "passed"
},
{
"width": 1920,
"dpr": 1,
"columns": 4,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-context/clusters-agroindustria-480.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/corredores-logisticos-v2-480.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/projectos-ancora-480.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/educacao-formacao-480.webp",
"width": 266,
"height": 149
},
{
"src": "http://127.0.0.1:5174/images/investment-context/documentos-formularios-480.webp",
"width": 266,
"height": 149
}
],
"status": "passed"
},
{
"fail": "primary",
"status": "passed"
},
{
"fail": "all",
"status": "passed"
}
],
"errors": [],
"navigation": [
"/clusters",
"/corredores",
"/projectos-ancora",
"/educacao",
"/documentos"
],
"databaseAccess": false
},
"lintExitCode": 0,
"buildExitCode": 0,
"pageAsset": "assets/PortalInvestidor-CYCMhSzW.js",
"screenshots": [
"outputs/investment-context-compact-20261023/cards-1024-dpr1.png",
"outputs/investment-context-compact-20261023/cards-1440-dpr2.png",
"outputs/investment-context-compact-20261023/cards-1920-dpr1.png",
"outputs/investment-context-compact-20261023/cards-320-dpr1.png",
"outputs/investment-context-compact-20261023/cards-390-dpr2.png",
"outputs/investment-context-compact-20261023/cards-768-dpr1.png"
],
"databaseChanged": false
}
}
@@ -0,0 +1,355 @@
{
"date": "2026-10-23",
"decision": "Pedido expresso da Tempo: adaptar hero, imagens e pesquisa da referência, preservando a informação e os comportamentos equivalentes do V2.",
"sourceProject": "D:/Projects/Tempo_Investidor/investidor",
"targetProject": "D:/Projects/Tempo_Investidor/Investidor-V2",
"planAndAnalysis": "docs/investmoz/public-portal-sections.md#adequação-do-hero--23102026",
"implementation": [
"resources/js/components/portal-hero.tsx",
"resources/js/pages/PortalInvestidor.tsx"
],
"assets": [
{
"source": "D:\\Projects\\Tempo_Investidor\\investidor\\public\\images\\hero\\hero-1.png",
"original": "public\\images\\hero\\hero-1.png",
"sha256": "3c9246169025b9b1b4d1d10fb51c1f644123984bde56c6c2d9bf869d851024fc",
"bytes": 2315768,
"webpBytes": 94012,
"mobileBytes": 38804
},
{
"source": "D:\\Projects\\Tempo_Investidor\\investidor\\public\\images\\hero\\hero-3.jpg",
"original": "public\\images\\hero\\hero-3.jpg",
"sha256": "91377213b6286aca7ee4af90860a7dc7a75fa08a165ef7b3192081db43f92dff",
"bytes": 9875137,
"webpBytes": 140342,
"mobileBytes": 32998
}
],
"desktopBytesTotal": 234354,
"mobileBytesTotal": 71802,
"originalBytesTotal": 12190905,
"verification": {
"browser": {
"results": [
{
"width": 320,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 390,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 768,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 1440,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 1920,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 1440,
"reducedMotion": "no-preference",
"status": "passed"
}
],
"errors": [],
"requests": [
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=",
"/oportunidades",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique"
],
"databaseAccess": false
},
"browserScript": "tools/portal-hero-qa.mjs",
"screenshots": [
"outputs/portal-hero-20261023/after-1440-no-preference.png",
"outputs/portal-hero-20261023/after-1440-reduce.png",
"outputs/portal-hero-20261023/after-1440-second-image.png",
"outputs/portal-hero-20261023/after-1920-reduce.png",
"outputs/portal-hero-20261023/after-320-reduce.png",
"outputs/portal-hero-20261023/after-390-reduce.png",
"outputs/portal-hero-20261023/after-768-reduce.png",
"outputs/portal-hero-20261023/before-1440-reduce.png",
"outputs/portal-hero-20261023/before-390-reduce.png"
],
"additionalCases": [
"pesquisa por Enter e botão",
"ordem do teclado: campo → botão",
"query acentuada e pontuação preservadas",
"remoção de espaços exteriores",
"pesquisa vazia abre catálogo",
"CTA abre oportunidades",
"API indisponível mantém indicadores indeterminados",
"contagens zero preservadas",
"duas imagens carregadas",
"selecção manual",
"pausa e retoma explícitas",
"foco pausa rotação",
"rotação automática após perda de foco",
"movimento reduzido",
"entrada documental e cabeçalho preservados"
],
"existingFrontendTests": {
"command": "node --test tests/frontend/portal-api.test.mjs tests/frontend/portal-navigation.test.mjs",
"passed": 20
},
"typecheck": {
"command": "npm run types:check",
"exitCode": 0
},
"lint": {
"files": [
"resources/js/components/portal-hero.tsx",
"resources/js/pages/PortalInvestidor.tsx",
"tools/portal-hero-qa.mjs"
],
"exitCode": 0
},
"qaBuild": {
"command": "INVESTMOZ_QA_BUILD=1 npm run build",
"exitCode": 0
},
"localBuild": {
"command": "npm run build",
"exitCode": 0,
"pageAsset": "assets/PortalInvestidor-MsUeKoJQ.js"
}
},
"matrix": {
"file": "docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv",
"referencesAdded": [
"MASTER-9dee08d0-0016",
"MASTER-efd550fd-0104",
"MASTER-55050f60-0001"
],
"rowCount": 3422,
"criteriaAndStatesPreserved": true
},
"scope": {
"appliedToLocalCodeAndBuild": true,
"activeDatabaseChanged": false,
"migrationsRun": false,
"externalDeploymentPerformed": false,
"backendSearchReimplemented": false
},
"limits": "Verificação visual com dados sintéticos; pesquisa continua limitada ao catálogo de oportunidades. Os critérios mais amplos da matriz permanecem no estado anterior.",
"latestRevision": {
"id": "hero-no-controls-20261023",
"date": "2026-10-23",
"decision": "Pedido expresso da Tempo: remover do hero as duas bolinhas e o botão de pausa das imagens.",
"implementation": "Removidos grupo de controlos, ícones Pause/Play e estado de pausa manual. Preservadas troca automática, escrita dinâmica, pesquisa e preferência de movimento reduzido.",
"browser": {
"results": [
{
"width": 320,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 390,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 768,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 1440,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 1920,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 390,
"reducedMotion": "no-preference",
"status": "passed"
},
{
"width": 1440,
"reducedMotion": "no-preference",
"status": "passed"
}
],
"errors": [],
"requests": [
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=",
"/oportunidades",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=",
"/oportunidades",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique"
],
"databaseAccess": false
},
"screenshots": [
"outputs/portal-hero-no-controls-20261023/after-1440-no-preference.png",
"outputs/portal-hero-no-controls-20261023/after-1440-reduce.png",
"outputs/portal-hero-no-controls-20261023/after-1920-reduce.png",
"outputs/portal-hero-no-controls-20261023/after-320-reduce.png",
"outputs/portal-hero-no-controls-20261023/after-390-no-preference.png",
"outputs/portal-hero-no-controls-20261023/after-390-reduce.png",
"outputs/portal-hero-no-controls-20261023/after-768-reduce.png",
"outputs/portal-hero-no-controls-20261023/second-image-1440.png",
"outputs/portal-hero-no-controls-20261023/second-image-390.png",
"outputs/portal-hero-no-controls-20261023/typed-1440.png",
"outputs/portal-hero-no-controls-20261023/typed-390.png"
],
"checks": {
"lintExitCode": 0,
"buildExitCode": 0,
"pageAsset": "assets/PortalInvestidor-T0B6aoV_.js"
},
"scope": {
"appliedLocally": true,
"databaseAccess": false,
"externalDeployment": false
}
},
"revisionHistory": [
{
"id": "hero-typing-20261023",
"date": "2026-10-23",
"decision": "Pedido expresso da Tempo: título e frase dinâmicos como na referência; remover Termos de uso do hero.",
"plan": [
"Analisar a sequência de escrita da referência.",
"Animar o título e depois a frase, mantendo os textos do V2 e reservando a dimensão final.",
"Reiniciar a escrita na troca de imagem e integrar pausa/retoma e preferência de movimento reduzido.",
"Remover o link e o ícone jurídico do hero.",
"Demonstrar comportamento em computador e telemóvel e compilar o pacote local."
],
"files": [
"resources/js/components/portal-hero.tsx",
"resources/css/app.css",
"tools/portal-hero-qa.mjs"
],
"browser": {
"results": [
{
"width": 320,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 390,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 768,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 1440,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 1920,
"reducedMotion": "reduce",
"status": "passed"
},
{
"width": 390,
"reducedMotion": "no-preference",
"status": "passed"
},
{
"width": 1440,
"reducedMotion": "no-preference",
"status": "passed"
}
],
"errors": [],
"requests": [
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=",
"/oportunidades",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique",
"/oportunidades?q=",
"/oportunidades",
"/oportunidades?q=energia%20%26%20Nampula%20%2F%20Mo%C3%A7ambique"
],
"databaseAccess": false
},
"screenshots": [
"outputs/portal-hero-typing-20261023/after-1440-no-preference.png",
"outputs/portal-hero-typing-20261023/after-1440-reduce.png",
"outputs/portal-hero-typing-20261023/after-1440-second-image.png",
"outputs/portal-hero-typing-20261023/after-1920-reduce.png",
"outputs/portal-hero-typing-20261023/after-320-reduce.png",
"outputs/portal-hero-typing-20261023/after-390-no-preference.png",
"outputs/portal-hero-typing-20261023/after-390-reduce.png",
"outputs/portal-hero-typing-20261023/after-768-reduce.png",
"outputs/portal-hero-typing-20261023/typed-1440.png",
"outputs/portal-hero-typing-20261023/typed-390.png",
"outputs/portal-hero-typing-20261023/typing-1440.png",
"outputs/portal-hero-typing-20261023/typing-390.png"
],
"demonstrated": [
"escrita progressiva",
"título antes da frase",
"texto completo no modo de movimento reduzido",
"nome acessível completo durante a escrita",
"pesquisa sem deslocamento durante a escrita",
"reinício na selecção manual e automática de imagem",
"pausa e retoma da escrita",
"cursor com pausa",
"Termos de uso ausente do hero",
"pesquisa, teclado, CTA, API indisponível e contagens zero preservados"
],
"checks": {
"types": {
"command": "npm run types:check",
"exitCode": 0
},
"lint": {
"command": "vp lint resources/js/components/portal-hero.tsx tools/portal-hero-qa.mjs",
"exitCode": 0
},
"build": {
"command": "npm run build",
"exitCode": 0,
"pageAsset": "assets/PortalInvestidor-BbVHeTeI.js",
"log": "outputs/portal-hero-typing-20261023/build-local.log"
}
},
"scope": {
"appliedLocally": true,
"databaseAccess": false,
"externalDeployment": false
}
}
]
}
+88
View File
@@ -48,3 +48,91 @@ Edge headless: 11 páginas públicas/autenticação verificadas a 390×844, com
Página inicial também verificada a 1440×1000. Cenário fiscal exercitado no navegador: base 1.000 × taxa 10% × (1 − redução 50%) = 50. Resultados em `public-portal-browser-validation.json`.
Antes da migration local foi criado e restaurado o backup de 74 tabelas em `storage/app/private/portal-services-backup-20260927-090641/`. O backup completo anterior permanece em `C:/laragon/backups/portal-investidor-20260927-101224/` e representa o estado anterior a esta entrega.
## Adequação do hero — 23/10/2026
Origem: pedido expresso da Tempo nesta sessão para aproveitar o hero, as imagens e a barra de pesquisa de `D:/Projects/Tempo_Investidor/investidor`, usando a informação existente no V2. Aplicam-se as orientações permanentes do `AGENTS.md`, incluindo a distinção entre decisão da Tempo e escolha técnica, a preservação de dados e a separação de QA e activo.
Análise prévia: a página de entrada real do V2 é `PortalInvestidor.tsx`, servida por `PortalServicesController::home`. A referência é `domains/investor-portal/components/homepage/homepage-hero.tsx`, montada em `InstitutionalHomepage`. A referência usa Inertia 2, Framer Motion, tradução e navegação diferentes; o V2 usa Inertia 3 e pesquisa de oportunidades. Copiar essas dependências e `/pesquisa` ou `/aviso-legal` directamente criaria incompatibilidades e destinos inexistentes.
Plano executado:
1. Registar a apresentação anterior em computador e telemóvel, com os componentes reais e dados sintéticos.
2. Copiar os dois originais efectivamente usados pela referência (`hero-1.png` e `hero-3.jpg`), conferir SHA-256 e gerar derivados WebP para computador e telemóvel. `hero-2.png` não faz parte da rotação da referência.
3. Extrair `components/portal-hero.tsx` e aplicar fotografias em rotação, texto centrado e pesquisa branca com botão vermelho. Preservar os textos do V2, contagens da API, sectores, documentos, apoio, cabeçalho e rodapé.
4. Manter a pesquisa de oportunidades em `/oportunidades?q=`, incluindo pesquisa vazia e caracteres acentuados. Conservar “Consultar oportunidades” e “Planear finanças”. A primeira iteração acrescentou o acesso jurídico `/termos-de-uso` ao hero; foi removido na revisão expressamente pedida pela Tempo abaixo.
5. Verificar apresentação, teclado, imagens, rotação, pausa, movimento reduzido, falha da API, contagens zero, TypeScript, lint e compilação. Compilar primeiro o pacote QA e depois os assets locais em `public/build`.
Escolhas técnicas desta adaptação: React e transições CSS existentes, sem novas dependências; rotação de oito segundos, pausa por controlo explícito, foco, ponteiro ou separador oculto; apresentação estática quando o utilizador pede movimento reduzido. A primeira iteração apresentou o texto estático; a revisão abaixo introduz a escrita dinâmica pedida pela Tempo. O campo indica pesquisa de oportunidades e conserva exemplos que o catálogo suporta; esta alteração não implementa uma pesquisa global de legislação/licenciamento.
Evidência reproduzível: `tools/portal-hero-qa.mjs`, com Vite local e Playwright configurável por `HERO_QA_PLAYWRIGHT`/`HERO_QA_CHROMIUM`. Fixtures e capturas em `outputs/portal-hero-20261023/`; síntese persistente em `public-portal-hero-validation.json`. Os testes de interface interceptam a API e a navegação Inertia, sem acesso a Laravel, base de dados ou serviços externos. A compilação local aplica os assets desta pasta; não representa publicação noutro servidor.
Referências na matriz existente: `MASTER-efd550fd-0104` (composição da página), `MASTER-9dee08d0-0016` (contexto da pesquisa) e `MASTER-55050f60-0001` (entrada documental preservada). Esta prova cobre a adaptação visual e os percursos exercitados; não encerra os critérios funcionais mais amplos dessas linhas.
### Revisão: escrita dinâmica e remoção do acesso jurídico no hero
Decisão expressa da Tempo na continuação da sessão de 23/10/2026: animar “INVISTA EM MOÇAMBIQUE” e “Transforme informação em decisões e ideias em negócios reais.” como na página de referência, e remover “Termos de uso” do hero. A anterior apresentação estática e a inclusão desse link eram escolhas de implementação, agora substituídas pelo pedido do proprietário.
Análise e plano executado: reproduzir a sequência da referência (espera inicial de 250 ms, título a 50 ms por carácter, intervalo de 150 ms e frase a 28 ms por carácter); reiniciar ao trocar de imagem; acrescentar o cursor intermitente; reservar a dimensão final de ambas as linhas para manter a pesquisa imóvel; remover o link e o ícone jurídico do hero; verificar em computador e telemóvel e compilar o pacote local.
Implementação em `portal-hero.tsx`, com React existente e CSS do cursor em `app.css`, sem dependências novas. A pausa explícita suspende também a escrita, retomando do mesmo ponto. Quando o separador fica oculto a escrita pára; a preferência de movimento reduzido apresenta os dois textos completos. Leitores de ecrã recebem o texto completo uma única vez, independentemente do número de caracteres visíveis. Os temporizadores são limpos ao mudar de imagem ou desmontar o componente.
O script `tools/portal-hero-qa.mjs` demonstra a escrita progressiva, a ordem título→frase, o reinício manual/automático, a pausa/retoma, a posição estável da pesquisa, o modo sem movimento e a ausência de “Termos de uso” no hero. As capturas e resultados desta revisão ficam em `outputs/portal-hero-typing-20261023/`, preservando a evidência anterior. Síntese e compilação final registadas na revisão de `public-portal-hero-validation.json`; referência acrescentada à linha existente `MASTER-efd550fd-0104`, sem encerrar o seu critério mais amplo.
### Revisão: remoção dos controlos visíveis das imagens
Pedido expresso da Tempo na continuação de 23/10/2026: remover do hero o grupo com o botão de pausa e as duas bolinhas de selecção de imagem. Removidos o grupo, os ícones e o estado de pausa manual. A troca automática de imagens e a escrita dinâmica permanecem; conservam-se a pausa da rotação por foco/ponteiro, a suspensão em separador oculto e a preferência de movimento reduzido. Esta decisão substitui os controlos visíveis introduzidos como escolha de implementação nas revisões anteriores.
O script existente de interface foi adaptado para demonstrar a ausência dos controlos e a continuação da rotação automática, escrita e pesquisa, com capturas em `outputs/portal-hero-no-controls-20261023/`. Evidência e pacote local registados em `public-portal-hero-validation.json`, mantendo o histórico das revisões e os estados dos requisitos da matriz.
## Fotografias nas etapas de Como Investir — 23/10/2026
Origem: pedido expresso da Tempo para analisar e ilustrar exclusivamente os cartões “1. Explorar”, “2. Preparar” e “3. Acompanhar”. Aplicam-se as orientações permanentes de `AGENTS.md` de 12/10 e 22/10/2026; componentes e optimização de recursos são escolhas técnicas, sem novas regras operacionais.
Análise e plano: os cartões existentes são âncoras estáticas na home React/Inertia, numa grelha de três colunas a partir de 768 px. Explorar liga a `/oportunidades` e representa a identificação de sector, território e oportunidade; Preparar liga a `/como-investir` e representa percurso/documentação; Acompanhar liga a `/balcao-virtual` e representa envio/consulta de pedido e protocolo. Os recursos locais existentes ilustram outras secções; preparar três fotografias distintas para evitar repetição. Gerar cenas editoriais plausíveis com `imagegen`, rever composição, guardar originais e versões WebP locais, integrar imagens 16:9 com dimensões reservadas e carregamento diferido, manter textos/destinos/título vermelho, verificar comportamento responsivo, navegação e falhas de imagem, compilar. Âmbito exclusivamente visual, sem alterações em dados, autenticação, apoio ou rodapé. Referência documental na linha existente `MASTER-efd550fd-0104`, sem criar outra matriz nem encerrar o critério mais amplo.
Implementação: `components/investment-steps.tsx` reúne os três cartões, preservando títulos, descrições e âncoras. Fotografias individuais geradas com a ferramenta integrada `image_gen`, sem local ou projecto real atribuído: avaliação de oportunidade num parque empresarial genérico, preparação documental numa reunião e consulta de pedido no computador. Originais em `outputs/investment-steps-20261023/originals/`; recursos locais em `public/images/investment-steps/` com WebP 960×540, 480×270 e alternativa própria 240×135. Os cartões usam `srcset`, `sizes`, `loading="lazy"`, dimensões reservadas, corte 16:9, alt em português, foco visível e texto compacto com 16 px de preenchimento. Se também falhar a alternativa pequena, o cartão mantém a área reservada, descrição acessível e destino utilizável.
Verificação desta secção: componentes reais com API sintética e sem base de dados, seis larguras de 320 a 1920 px, duas densidades de ecrã, três imagens distintas, texto/destinos conservados, navegação por teclado, ausência de transbordo e deslocamento no carregamento, falhas dos recursos principais e de todos os recursos. Oito cenários sem erros JavaScript. Fotografias, prompts, hashes, capturas, relatório de browser e compilação documentados em `public-portal-steps-images.json` e `outputs/investment-steps-20261023/`. Ajuste restrito a esta secção; apoio, acesso à gestão e rodapé mantidos.
## Fotografias nos cartões de contexto do investimento — 23/10/2026
Origem: pedido expresso da Tempo para aplicar o anexo “Melhorar as imagens e a identidade visual do Portal do Investidor de Moçambique” exclusivamente à secção “Encontre o contexto do seu investimento”. O anexo define estética fotográfica editorial, imagens individuais contextualizadas, recursos locais, acessibilidade, desempenho e preservação dos cinco destinos e conteúdos. Aplicam-se as orientações permanentes de `AGENTS.md` de 12/10 e 22/10/2026; os pormenores de implementação abaixo são escolhas técnicas.
Análise prévia: Laravel/Inertia 3 e React, com Tailwind existente; os cinco cartões estavam embutidos em `pages/PortalInvestidor.tsx`. `EducationalArticleCard` destina-se a artigos com acções e metadados diferentes, pelo que não foi alterado. A secção foi extraída para `components/investment-context-cards.tsx`, mantendo o título, descrições, âncoras e grelha de uma/duas/quatro colunas. Não foram alteradas as páginas de destino, dados, APIs ou estilos globais.
Foram geradas cinco imagens distintas com a ferramenta integrada `image_gen`, segundo a skill `imagegen`: agro-indústria, terminal logístico, acompanhamento de instalação solar, formação técnica e análise documental. São imagens fotográficas ilustrativas geradas, sem localização ou instituição real atribuída, e não fotografias documentais de projectos cadastrados. A imagem do terminal foi revista para remover o camião com marca visível. Não foram usadas fotografias externas nem URLs de serviços de imagens. Originais seleccionados, prompts e hashes estão registados no manifesto `public-portal-context-images.json`; os originais foram preservados dentro do workspace.
Recursos finais em `public/images/investment-context/`: `clusters-agroindustria`, `corredores-logisticos-v2`, `projectos-ancora`, `educacao-formacao` e `documentos-formularios`, em WebP 960×540, com variantes 480×270 e alternativas 240×135. As imagens mantêm 16:9, `object-cover`, textos alternativos em português, dimensões reservadas, `srcset`, `sizes`, `loading="lazy"` e descodificação assíncrona. Cada cartão conserva o seu título e descrição; o foco usa o azul e os tons neutros pedidos no anexo.
Se um recurso principal falhar, o cartão usa a sua própria variante pequena, evitando repetir a fotografia de outra secção. Se também essa falhar, apresenta “Imagem indisponível” no mesmo espaço reservado, com descrição acessível e link operacional. Este é apenas um estado de erro; os cinco cartões usam imagens fotográficas na apresentação normal.
Verificação reproduzível: `tools/portal-context-cards-qa.mjs`, com o portal e cabeçalho reais, API sintética e acesso exclusivo aos recursos locais. Exercita seis larguras de ecrã, ecrãs de alta densidade, os cinco destinos por teclado, imagens carregadas e distintas, alinhamento, dimensões, ausência de deslocamento no carregamento e dois cenários de falha. Capturas e resultados em `outputs/investment-context-20261023/`; síntese, fontes/prompts e compilação em `public-portal-context-images.json`. Referências acrescentadas às linhas existentes `MASTER-efd550fd-0104` e `MASTER-55050f60-0001`, sem encerrar os respectivos critérios mais amplos.
Revisão expressa da Tempo de 23/10/2026: título da secção no vermelho da identidade existente (`#ed1c24`) e cartões mais compactos. Removidas as alturas mínimas de 56 e 72 px do título/descrição; reduzidos o preenchimento de 20 para 16 px, o intervalo de 8 para 6 px e as entrelinhas. As alturas passam a acompanhar o conteúdo, com alinhamento por linha da grelha, conservando imagens, textos e destinos. A reserva das dimensões das imagens mantém-se. Evidência desta revisão em `outputs/investment-context-compact-20261023/` e no campo `layoutRevision` do manifesto existente.
## Sectores da home e catálogo completo — 23/10/2026
Origem: pedido expresso da Tempo para adaptar a estrutura de “Áreas de Investimento” do projecto `D:/Projects/Tempo_Investidor/tempo-dashboard`, incluindo cartões, botão para mais sectores e respectiva página, e uniformizar a vermelho os títulos das secções da home. Aplicadas as orientações permanentes `AGENTS.md` de 12/10 e 22/10/2026: as decisões de produto são da Tempo; as escolhas técnicas abaixo não criam requisitos organizacionais.
Análise prévia: a referência usa `components/investor-portal/homepage/homepage-sectors.tsx` e `pages/investor-portal/sectors/index.tsx`. Mostra cinco cartões com fotografia e título sobreposto, resumo, “Saiba mais” e ligação central arredondada para o catálogo. O V2 já dispunha de `/sectores`, fichas `/sectores/{slug}`, especialização Fintech e dados de `SectorOrganization`. Foram reutilizados esses contratos e percursos, sem importar Framer Motion, traduções ou rotas exclusivas da referência.
Implementação: `components/public-sector-cards.tsx` partilha os cartões entre a home e o catálogo. A home mostra os primeiros cinco sectores, na ordem recebida da API, com “Ler mais” para a ficha existente e “Ver mais sectores” para `/sectores`. A página existente apresenta todos os sectores publicados, na mesma estrutura visual, e “Voltar ao início”. Mantêm-se nomes, descrições, contagens, valores zero, especializações e URLs canónicos recebidos. Descrições ausentes não são preenchidas com dados da referência. Fintech permanece ligado à ficha própria, sem âncoras HTML aninhadas.
Foram inventariados os onze JPG disponíveis em `storage/app/public/investor-portal/sectors` na referência, copiados os originais para evidência e preparados derivados locais WebP 960×540/480×270 em `public/images/sectors/`. O mapeamento visual por slug é de apresentação e não altera a taxonomia. Para Imobiliário e sectores sem imagem específica usa-se a vista empresarial genérica disponível na referência; para Bens de Consumo e Franquias usa-se a imagem de actividade industrial. A variante genérica também serve como alternativa em caso de erro; se ambas falharem, o título e as ligações continuam visíveis num fundo neutro. Não se atribui aos ficheiros uma origem oficial, licença externa ou localização documental não comprovada.
Os seis títulos de secção da home — contexto do investimento, sectores, Como Investir, oportunidades em destaque, documentos essenciais e apoio — usam `HOME_SECTION_HEADING` em `lib/portal-presentation.ts`: vermelho `#ed1c24`, 24 px, entrelinha de 32 px e peso 700. O cartão de Apoio passou a superfície clara para manter contraste com o título vermelho. O título principal animado do hero mantém a sua hierarquia própria. `PORTAL_MORE_LINK` centraliza o botão arredondado, contorno vermelho e estado de foco, seguindo a referência.
Verificação: `tools/portal-home-sectors-qa.mjs` carrega os componentes reais com dez sectores sintéticos, descrições nulas e especialização Fintech, sem base de dados. Exercita seis larguras, prévia de cinco/cartálogo de dez, estilos dos seis títulos, percurso por teclado, dez fichas, Fintech, regresso à home, catálogo vazio e dois cenários de indisponibilidade de imagens. Capturas, inventário, hashes e resultados em `outputs/home-sectors-20261023/`; resumo persistente em `public-portal-sectors-validation.json`. Referências na matriz existente: `MASTER-efd550fd-0104` e `MASTER-9f5719c8-0077`. Esta adaptação não encerra critérios mais amplos nem altera dados, permissões, regras operacionais ou publicação noutro servidor.
Pedido expresso da Tempo, 23/10/2026 — botão “Contactar Apoio ao Investidor”: em `layouts/portal-navigation.tsx`, a faixa de largura total foi substituída por um botão centrado, com largura ajustada ao texto, altura mínima de 44 px, preenchimento reduzido, sombra discreta e vermelho `#ed1c24`, conservando `/apoio` e o foco visível. Verificado a 320, 390 e 1440 px: cerca de 223×44 px, sem transbordo ou erros JavaScript. Evidência em `outputs/support-button-20261023/qa-results.json` e capturas; compilação local no mesmo directório. Ajuste de apresentação por decisão do proprietário, sem nova regra operacional nem alteração de dados.
Revisão anterior de 23/10/2026, superada pela correcção de âmbito abaixo: o Codex interpretou o pedido de abreviação como aplicável à zona inferior inteira e alterou indevidamente o botão de apoio e o rodapé partilhado. Essa interpretação não foi uma decisão da Tempo. A revisão removeu o bloco interno, abreviou o botão para “Apoio” e recolheu contactos/ligações em “Informações” num rodapé claro; estas alterações fora do bloco indicado foram revertidas após a clarificação expressa do proprietário. O registo desta revisão é histórico e não descreve a apresentação actual.
Evidência histórica da revisão superada: `outputs/footer-compact-20261023/qa-results.json`, capturas e `check.mjs`. Não constitui aceitação do âmbito pelo proprietário.
Correcção expressa da Tempo, 23/10/2026: o pedido abrange exclusivamente o bloco “INVESTE Moçambique / Portal do Investidor — conheça as oportunidades… / Acesso à gestão”. Em `pages/PortalInvestidor.tsx`, esse bloco fica abreviado num ícone de acesso à gestão, com nome acessível e descrição “Acesso à gestão”, destino `/acesso-gestao`, área de interacção de 44 px e fundo branco, sem o título e parágrafo anteriores. O botão de apoio foi restaurado exactamente à apresentação anterior à abreviação: “Contactar Apoio ao Investidor”, compacto, vermelho e sem ícone. `components/tempo-footer.tsx` e os estilos `.tempo-footer` foram integralmente restaurados ao estado anterior, incluindo contactos e ligações visíveis e o fundo original do rodapé geral. Não se altera autenticação nem dados. Esta correcção substitui a interpretação indevida acima e mantém as restantes alterações previamente pedidas.
Evidência da correcção: `outputs/footer-scope-correction-20261023/check.mjs`, `qa-results.json`, capturas e `build-local.log`; componentes reais com API sintética a 320, 390 e 1440 px, ícone de gestão focável, bloco específico claro sem parágrafo, botão de apoio completo/vermelho, rodapé geral restaurado, contactos e ligações legais visíveis, sem transbordo ou erros JavaScript. Lint dos componentes alterados e compilação local. Referência na linha existente `MASTER-efd550fd-0104`, sem encerramento do critério amplo nem alteração dos seus estados.
Continuação expressa da Tempo, 23/10/2026: acrescentado o título visível “Acesso à gestão” ao lado do ícone nesse mesmo bloco, para tornar o destino compreensível. O acesso mantém-se compacto, vermelho e com fundo claro, conservando `/acesso-gestao`. Ajuste restrito a essa ligação; apoio e rodapé geral conservam a apresentação restaurada. Referência: `MASTER-efd550fd-0104` e a evidência da revisão anterior acima. Verificado a 320, 390 e 1440 px, sem transbordo ou erros JavaScript; lint e compilação aprovados. Evidência em `outputs/management-label-20261023/qa-results.json`, capturas e `build-local.log`. No pedido seguinte, a Tempo determinou o alinhamento desse acesso à esquerda; aplicado `justify-start` apenas no seu contentor.
@@ -0,0 +1,748 @@
{
"date": "2026-10-23",
"decision": "Pedido expresso da Tempo: adaptar Áreas de Investimento do tempo-dashboard aos sectores da home e ao catálogo completo, copiar a estrutura dos botões e uniformizar a vermelho os títulos de secção da home.",
"reference": {
"project": "D:/Projects/Tempo_Investidor/tempo-dashboard",
"files": [
"resources/js/components/investor-portal/homepage/homepage-sectors.tsx",
"resources/js/pages/investor-portal/sectors/index.tsx"
],
"structure": "Cinco cartões com fotografia, título sobreposto, resumo e acção de leitura; botão central arredondado/contorno vermelho para o catálogo."
},
"analysisAndPlan": "docs/investmoz/public-portal-sections.md#sectores-da-home-e-catálogo-completo--23102026",
"filesChanged": [
"resources/js/components/public-sector-cards.tsx",
"resources/js/lib/portal-presentation.ts",
"resources/js/components/investment-context-cards.tsx",
"resources/js/lib/portal-api.ts",
"resources/js/pages/PortalInvestidor.tsx",
"resources/js/pages/portal/services.tsx",
"tools/portal-home-sectors-qa.mjs",
"docs/investmoz/public-portal-sections.md",
"docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv"
],
"behaviour": {
"homePreview": 5,
"previewOrder": "Ordem recebida da API, sem novos filtros de publicação.",
"readMore": "Ler mais, para a URL recebida ou ficha existente /sectores/{slug}.",
"moreSectors": "Ver mais sectores, botão arredondado para /sectores.",
"catalogue": "Todos os sectores publicados devolvidos pelo cadastro existente; Voltar ao início para /.",
"specializations": "Fintech conservado como ligação própria; sem âncoras aninhadas.",
"countsAndDescriptions": "Preservados dados recebidos, incluindo zero e descrições ausentes; não importados textos ou registos do outro projecto.",
"headings": [
{
"title": "Encontre o contexto do seu investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Sectores de investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Como Investir",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Oportunidades em destaque",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Documentos essenciais",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Apoio ao Investidor",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
}
],
"supportCard": "Superfície clara para contraste com o título vermelho."
},
"assets": [
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\agronegocio.jpg",
"sourceBytes": 444057,
"original": "outputs/home-sectors-20261023/originals/agronegocio.jpg",
"sha256": "7337b689a6ad8b0b4c8d716ea356db14e22bd2982089825bb2382121ed3482f6",
"derivatives": [
{
"file": "public/images/sectors/agronegocio.webp",
"width": 960,
"height": 540,
"bytes": 110310,
"sha256": "3e9d3be22556300f880be39837f86e764e3d05433beff1da6694ef6cee1d5c34"
},
{
"file": "public/images/sectors/agronegocio-480.webp",
"width": 480,
"height": 270,
"bytes": 31592,
"sha256": "fd1f7624f7fd9224fc1c7bd11746806e419c645ba43c257b1edd9ba250cd47f1"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\economia-azul.jpg",
"sourceBytes": 456892,
"original": "outputs/home-sectors-20261023/originals/economia-azul.jpg",
"sha256": "1a2c04d0d9ed4a26129553daefd2ee18f6d820026ab1203acf3bcf91b0550f69",
"derivatives": [
{
"file": "public/images/sectors/economia-azul.webp",
"width": 960,
"height": 540,
"bytes": 107626,
"sha256": "bf3751e66839c1b3c585c91ffe65d32b9ee79ebd7d08e5e2c95c6cd581b04f8f"
},
{
"file": "public/images/sectors/economia-azul-480.webp",
"width": 480,
"height": 270,
"bytes": 29350,
"sha256": "aaf8ee66709d8e15c4304690dd03830b4368d14468a597d5617ccdb71d614331"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\energia.jpg",
"sourceBytes": 386678,
"original": "outputs/home-sectors-20261023/originals/energia.jpg",
"sha256": "a164e2c8ef155a6a198ae400feca132e79ba75e022473893ec48ebfa0557f679",
"derivatives": [
{
"file": "public/images/sectors/energia.webp",
"width": 960,
"height": 540,
"bytes": 81236,
"sha256": "b5605d9bb8645c5042baa511aaf39fcb187080a48d8e067b130fc974c4c9196d"
},
{
"file": "public/images/sectors/energia-480.webp",
"width": 480,
"height": 270,
"bytes": 21926,
"sha256": "7e53977fc6ce11755455f8092ac3f8c7126f54d909a3935e522f8dae0ab834be"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\industria.jpg",
"sourceBytes": 357131,
"original": "outputs/home-sectors-20261023/originals/industria.jpg",
"sha256": "d70c4257ac58a4b5e9f8b8b1a4cf6d98c0514043442c3ce46f5c98399038527f",
"derivatives": [
{
"file": "public/images/sectors/industria.webp",
"width": 960,
"height": 540,
"bytes": 80422,
"sha256": "ca8ddd2d65647f1ed7a9906271fa2ea3d027f310b89434fd7781a37a3d18322d"
},
{
"file": "public/images/sectors/industria-480.webp",
"width": 480,
"height": 270,
"bytes": 25194,
"sha256": "8bd695231209c0042b94e68b0e2095fa7202297708dabff0f78b337500b8c9b7"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\infraestruturas.jpg",
"sourceBytes": 395383,
"original": "outputs/home-sectors-20261023/originals/infraestruturas.jpg",
"sha256": "ac6cde89507af68991faebee19540f955146724d3be901839b85a4098fe1674b",
"derivatives": [
{
"file": "public/images/sectors/infraestruturas.webp",
"width": 960,
"height": 540,
"bytes": 86654,
"sha256": "14754a59477e5c62a62c190be812c89d9616608b3c98acb6100ad70d94d34df0"
},
{
"file": "public/images/sectors/infraestruturas-480.webp",
"width": 480,
"height": 270,
"bytes": 25772,
"sha256": "eca58096087d05dae3eea2b4d063c103f9fc85b0ea20fc5051313cf9bfa1020f"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\logistica-transportes.jpg",
"sourceBytes": 413702,
"original": "outputs/home-sectors-20261023/originals/logistica-transportes.jpg",
"sha256": "5976ead23da0d4e7d5debf875262f0229d389826aaeb7e7be4ebbac3f5129a9e",
"derivatives": [
{
"file": "public/images/sectors/logistica-transportes.webp",
"width": 960,
"height": 540,
"bytes": 96254,
"sha256": "0d235e7bbd160cccd3f97daa3337d6b27fde49db9782ad2ba0599c6a83022b6c"
},
{
"file": "public/images/sectors/logistica-transportes-480.webp",
"width": 480,
"height": 270,
"bytes": 28816,
"sha256": "32f5137e0a7f6a7894b186edb2ca38f12cdfdca04dda3588de3969fb3d2e333a"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\mineracao.jpg",
"sourceBytes": 415161,
"original": "outputs/home-sectors-20261023/originals/mineracao.jpg",
"sha256": "7e6d1e5a17d434456ad937680a6c5501bd33bd624d2cc7ebf4b643185a9440aa",
"derivatives": [
{
"file": "public/images/sectors/mineracao.webp",
"width": 960,
"height": 540,
"bytes": 96642,
"sha256": "12c93df378f54108b13260c10d29789564af7844cc84bcd9281d2be917d83354"
},
{
"file": "public/images/sectors/mineracao-480.webp",
"width": 480,
"height": 270,
"bytes": 26034,
"sha256": "be3e7763fb8cd2e02f576204269821c40d93a7c155d8088a5084064b7269e4d6"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\outros-sectores.jpg",
"sourceBytes": 328152,
"original": "outputs/home-sectors-20261023/originals/outros-sectores.jpg",
"sha256": "4aaac0869dc7e61c4c7a87e1af95beb108aa9be0da082702f6f70722e62b1e7a",
"derivatives": [
{
"file": "public/images/sectors/outros-sectores.webp",
"width": 960,
"height": 540,
"bytes": 75414,
"sha256": "f47b3d0da5b0cd5153d393c6f779484ba89043e50195e968338108e893a88077"
},
{
"file": "public/images/sectors/outros-sectores-480.webp",
"width": 480,
"height": 270,
"bytes": 24936,
"sha256": "a4fc00551d75c96a1648ad4f81d759d4d6ecd7e6fc5689689216a96934bd1ea2"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\servicos-financeiros.jpg",
"sourceBytes": 348386,
"original": "outputs/home-sectors-20261023/originals/servicos-financeiros.jpg",
"sha256": "f932a56248d5544bff92aa30f4af3a5a63044c311969d4370d06f157b097d570",
"derivatives": [
{
"file": "public/images/sectors/servicos-financeiros.webp",
"width": 960,
"height": 540,
"bytes": 72144,
"sha256": "15379485db7de7b2efebe8bdbd4cbd8e911eb34ab8a23b3dd114c849bb4ae65c"
},
{
"file": "public/images/sectors/servicos-financeiros-480.webp",
"width": 480,
"height": 270,
"bytes": 20698,
"sha256": "c21949201e35e63625d3d2e7520e10912fe65be1186954142b2784f6efca51ed"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\tic-economia-digital.jpg",
"sourceBytes": 417398,
"original": "outputs/home-sectors-20261023/originals/tic-economia-digital.jpg",
"sha256": "b09703bd079f55fb5a8ec3167ce96e1fdb1e5090813a81c817b9a33909427d94",
"derivatives": [
{
"file": "public/images/sectors/tic-economia-digital.webp",
"width": 960,
"height": 540,
"bytes": 97892,
"sha256": "b1d79c1a941085582d6a72799140bafed1bf38ec0201a4dfe5f6faf54ea55ae8"
},
{
"file": "public/images/sectors/tic-economia-digital-480.webp",
"width": 480,
"height": 270,
"bytes": 28242,
"sha256": "c4279a27ed402ca2feb62f11a8a954cbcae993912d3c8c27a2d9644fdf922114"
}
]
},
{
"source": "D:\\Projects\\Tempo_Investidor\\tempo-dashboard\\storage\\app\\public\\investor-portal\\sectors\\turismo.jpg",
"sourceBytes": 393339,
"original": "outputs/home-sectors-20261023/originals/turismo.jpg",
"sha256": "1a76db252bc8c1a9dc1f3c650bd95e55337c36a6f604753d61cad96c19b1875f",
"derivatives": [
{
"file": "public/images/sectors/turismo.webp",
"width": 960,
"height": 540,
"bytes": 93234,
"sha256": "de46707dfdd8b98c48ec88d19f3df0b1d6c130adfcbd97d5a7c2e6fa2db62bef"
},
{
"file": "public/images/sectors/turismo-480.webp",
"width": 480,
"height": 270,
"bytes": 27012,
"sha256": "4d3d9a04b9426474dca9ba8895021b48fffbe7e4d4a4551272b8c832cf83488e"
}
]
}
],
"assetMapping": {
"agronegocio": "agronegocio",
"turismo": "turismo",
"infraestruturas": "infraestruturas",
"economia-azul": "economia-azul",
"mercado-financeiro": "servicos-financeiros",
"mineracao": "mineracao",
"energia-especifica": "energia",
"imobiliario": "outros-sectores",
"bens-de-consumo-e-franquias": "industria",
"tecnologias-de-informacao": "tic-economia-digital"
},
"assetSource": "JPG locais do projecto de referência indicado pelo proprietário, inventariados e copiados sem alteração; WebP derivados para apresentação. Sem alegação adicional de licença externa, origem oficial ou localização documental. Imagem genérica reutilizada como alternativa funcional e para sectores sem imagem específica.",
"assetBytes960": 997828,
"assetBytes480": 289572,
"verification": {
"browser": {
"results": [
{
"width": 320,
"homeSectors": 5,
"catalogueSectors": 10,
"homeColumns": 2,
"headings": [
{
"title": "Encontre o contexto do seu investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Sectores de investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Como Investir",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Oportunidades em destaque",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Documentos essenciais",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Apoio ao Investidor",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
}
],
"status": "passed"
},
{
"width": 390,
"homeSectors": 5,
"catalogueSectors": 10,
"homeColumns": 2,
"headings": [
{
"title": "Encontre o contexto do seu investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Sectores de investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Como Investir",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Oportunidades em destaque",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Documentos essenciais",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Apoio ao Investidor",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
}
],
"status": "passed"
},
{
"width": 768,
"homeSectors": 5,
"catalogueSectors": 10,
"homeColumns": 3,
"headings": [
{
"title": "Encontre o contexto do seu investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Sectores de investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Como Investir",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Oportunidades em destaque",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Documentos essenciais",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Apoio ao Investidor",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
}
],
"status": "passed"
},
{
"width": 1024,
"homeSectors": 5,
"catalogueSectors": 10,
"homeColumns": 5,
"headings": [
{
"title": "Encontre o contexto do seu investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Sectores de investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Como Investir",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Oportunidades em destaque",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Documentos essenciais",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Apoio ao Investidor",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
}
],
"status": "passed"
},
{
"width": 1440,
"homeSectors": 5,
"catalogueSectors": 10,
"homeColumns": 5,
"headings": [
{
"title": "Encontre o contexto do seu investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Sectores de investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Como Investir",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Oportunidades em destaque",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Documentos essenciais",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Apoio ao Investidor",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
}
],
"status": "passed"
},
{
"width": 1920,
"homeSectors": 5,
"catalogueSectors": 10,
"homeColumns": 5,
"headings": [
{
"title": "Encontre o contexto do seu investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Sectores de investimento",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Como Investir",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Oportunidades em destaque",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Documentos essenciais",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
},
{
"title": "Apoio ao Investidor",
"size": "24px",
"weight": "700",
"color": "rgb(237, 28, 36)",
"lineHeight": "32px"
}
],
"status": "passed"
},
{
"fail": "primary",
"status": "passed"
},
{
"fail": "all",
"status": "passed"
},
{
"emptyCatalogue": true,
"status": "passed"
}
],
"errors": [],
"navigation": [
"/sectores",
"/sectores",
"/sectores",
"/sectores",
"/sectores",
"/sectores/agronegocio",
"/sectores",
"/sectores/turismo",
"/sectores",
"/sectores/infraestruturas",
"/sectores",
"/sectores/economia-azul",
"/sectores",
"/sectores/mercado-financeiro",
"/sectores",
"/sectores/mineracao",
"/sectores",
"/sectores/energia-especifica",
"/sectores",
"/sectores/imobiliario",
"/sectores",
"/sectores/bens-de-consumo-e-franquias",
"/sectores",
"/sectores/tecnologias-de-informacao",
"/sectores",
"/sectores/mercado-financeiro/fintech",
"/sectores",
"/",
"/sectores",
"/sectores"
],
"databaseAccess": false
},
"script": "tools/portal-home-sectors-qa.mjs",
"screenshots": [
"outputs/home-sectors-20261023/catalogue-1024.png",
"outputs/home-sectors-20261023/catalogue-1440.png",
"outputs/home-sectors-20261023/catalogue-1920.png",
"outputs/home-sectors-20261023/catalogue-320.png",
"outputs/home-sectors-20261023/catalogue-390.png",
"outputs/home-sectors-20261023/catalogue-768.png",
"outputs/home-sectors-20261023/fallback-all.png",
"outputs/home-sectors-20261023/fallback-primary.png",
"outputs/home-sectors-20261023/home-sectors-1024.png",
"outputs/home-sectors-20261023/home-sectors-1440.png",
"outputs/home-sectors-20261023/home-sectors-1920.png",
"outputs/home-sectors-20261023/home-sectors-320.png",
"outputs/home-sectors-20261023/home-sectors-390.png",
"outputs/home-sectors-20261023/home-sectors-768.png",
"outputs/home-sectors-20261023/homepage-1440.png"
],
"existingFrontendTests": {
"command": "node --test tests/frontend/portal-api.test.mjs tests/frontend/portal-navigation.test.mjs",
"passed": 20
},
"types": {
"command": "npm run types:check",
"exitCode": 0
},
"lint": {
"exitCode": 0
},
"build": {
"command": "npm run build",
"exitCode": 0,
"assets": {
"resources/js/pages/PortalInvestidor.tsx": "assets/PortalInvestidor-Q1OJxvfZ.js",
"resources/js/pages/portal/services.tsx": "assets/services-DiuG4Zkv.js"
},
"log": "outputs/home-sectors-20261023/build-local.log"
}
},
"scope": {
"appliedLocally": true,
"databaseChanged": false,
"migrationsRun": false,
"businessRulesChanged": false,
"routesChanged": false,
"sourceProjectChanged": false,
"newDependencies": false,
"externalDeployment": false
},
"limits": "Interface e navegação exercitadas com API e páginas públicas em fixtures; não equivale a demonstração integral dos workflows operacionais ou de critérios mais amplos da matriz.",
"matrixReferences": [
"MASTER-efd550fd-0104",
"MASTER-9f5719c8-0077"
]
}
@@ -0,0 +1,406 @@
{
"date": "2026-10-23",
"decision": "Pedido expresso da Tempo: analisar e acrescentar imagens adequadas exclusivamente aos três cartões de Como Investir.",
"analysisAndPlan": "docs/investmoz/public-portal-sections.md#fotografias-nas-etapas-de-como-investir--23102026",
"source": {
"type": "Fotografias ilustrativas geradas por IA, sem atribuição a pessoas, instituições, locais ou projectos reais.",
"tool": "image_gen, modo integrado",
"skill": "C:/Users/jorge/.codex/skills/.system/imagegen/SKILL.md",
"externalPhotographsUsed": false
},
"images": [
{
"key": "explorar",
"prompt": "Use case: photorealistic-natural. Asset for a small navigation card on a Mozambique investor portal. Create one individual photorealistic editorial photograph, horizontal 16:9, professional restrained style, natural soft daylight, realistic skin and materials, neutral colors, documentary composition readable at 350px wide. Illustration of a plausible setting, no identifiable named project or official building. No text, lettering, logos, watermarks, flags, maps, diagrams, graphic overlays, montage, or advertisement styling. Keep main subject inside the central 80% for responsive crop. Stage 1 EXPLORE opportunities, sector and territory: two Mozambican business professionals, a woman and man in smart casual clothing, standing outdoors by a quiet access road at a modest business park, naturally discussing a tablet held by the woman. Generic unbranded low warehouse buildings and green productive land visible in background, credible southern African environment. Eye-level medium-wide candid composition, both people three-quarter view, looking at tablet and surroundings rather than camera. Business evaluation and discovery, no construction work or hard hats.",
"source": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-42eb9338-4350-40bf-9098-858c12994189.png",
"original": "outputs/investment-steps-20261023/originals/explorar.png",
"originalSha256": "a3c4ec1a0d2deaf87e0c8e765616c74ab70067eade9a862effce9e6befbbccb6",
"originalDimensions": {
"width": 1672,
"height": 941
},
"derivatives": [
{
"file": "public/images/investment-steps/explorar.webp",
"width": 960,
"height": 540,
"bytes": 52266,
"sha256": "c3e151ae649ce3cb66f5babc39978fa5803d320cacc95a27e798db4ec2d45a4c"
},
{
"file": "public/images/investment-steps/explorar-480.webp",
"width": 480,
"height": 270,
"bytes": 19312,
"sha256": "d3f383d9a58b23fada37472ea853f985aca8c3146123747d777b27704d32c7e0"
},
{
"file": "public/images/investment-steps/explorar-fallback.webp",
"width": 240,
"height": 135,
"bytes": 5300,
"sha256": "ba12ac4d2e81ac8a2c2e681667ccb08a3abfdba24de39e6e942d6378b700f2a9"
}
]
},
{
"key": "preparar",
"prompt": "Use case: photorealistic-natural. Asset for a small navigation card on a Mozambique investor portal. Create one individual photorealistic editorial photograph, horizontal 16:9, professional restrained style, natural soft daylight, realistic skin and materials, neutral colors, documentary composition readable at 350px wide. Illustration of a plausible setting, no identifiable named project or official building. No text, lettering, logos, watermarks, flags, maps, diagrams, graphic overlays, montage, or advertisement styling. Keep main subject inside the central 80% for responsive crop. Stage 2 PREPARE investment and organize documents: a Mozambican woman entrepreneur and a male business adviser seated at a light wood office desk, calmly reviewing and sorting a small tidy set of document folders and loose sheets beside a closed laptop. One points to a page while the other organizes a folder. No visible readable writing or personal data; papers at an angle. Bright modest professional meeting room, view from slightly above table height, hands and folders clear in foreground, natural candid interaction, realistic anatomy.",
"source": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-5f07618a-1e86-42d9-aaf8-3acd0abf2164.png",
"original": "outputs/investment-steps-20261023/originals/preparar.png",
"originalSha256": "679a6875bb3ec79a9b71c27c9d785334376746be2bb305f461db9e19b0a0b7ea",
"originalDimensions": {
"width": 1672,
"height": 941
},
"derivatives": [
{
"file": "public/images/investment-steps/preparar.webp",
"width": 960,
"height": 540,
"bytes": 57942,
"sha256": "6de28042d3abce238a3680fb878779682f3672c3feb97b7e42b72e9a0a0ffbac"
},
{
"file": "public/images/investment-steps/preparar-480.webp",
"width": 480,
"height": 270,
"bytes": 19950,
"sha256": "671ba0fc207f85833b91027d3f6dad91599e95702cfea439e558906f92a2bfac"
},
{
"file": "public/images/investment-steps/preparar-fallback.webp",
"width": 240,
"height": 135,
"bytes": 6008,
"sha256": "645b34cd60b31fdc8e6586dedb71061c08add9ff985ce90909333b0bd1e0aa3e"
}
]
},
{
"key": "acompanhar",
"prompt": "Use case: photorealistic-natural. Asset for a small navigation card on a Mozambique investor portal. Create one individual photorealistic editorial photograph, horizontal 16:9, professional restrained style, natural soft daylight, realistic skin and materials, neutral colors, documentary composition readable at 350px wide. Illustration of a plausible setting, no identifiable named project or official building. No text, lettering, logos, watermarks, flags, maps, diagrams, graphic overlays, montage, or advertisement styling. Keep main subject inside the central 80% for responsive crop. Stage 3 FOLLOW UP submitted request and protocol: a Mozambican male entrepreneur seated at a tidy desk using a laptop to check an online application, one hand naturally on trackpad, a small closed document folder nearby. Three-quarter side view showing focused face and laptop, warm daylight from side window, modest contemporary office with soft greenery out of focus. Laptop screen is oblique and softly out of focus with only indistinct neutral interface rows, no readable text, official seal, logo or invented dashboard details. Convey calm digital self-service and progress checking, not a call centre or signing documents.",
"source": "C:\\Users\\jorge\\.codex\\generated_images\\01a167bd-3f26-71a0-8ec3-3f17519d2bfd\\exec-f896c5f8-19a0-49b0-af8e-0d12bfe4c7aa.png",
"original": "outputs/investment-steps-20261023/originals/acompanhar.png",
"originalSha256": "17d453445f2afb4c7641909950d2ecfb8276c433a4e28eeee94d0a57d1bdad46",
"originalDimensions": {
"width": 1672,
"height": 941
},
"derivatives": [
{
"file": "public/images/investment-steps/acompanhar.webp",
"width": 960,
"height": 540,
"bytes": 43722,
"sha256": "fe764c8161de36ee597f856face5341260670d1a28ab482bd01a0f5120ad0588"
},
{
"file": "public/images/investment-steps/acompanhar-480.webp",
"width": 480,
"height": 270,
"bytes": 16176,
"sha256": "79ceaecb0ef23c6729e43d04b7e91493d6784ea9f658d540dda3550702dc3623"
},
{
"file": "public/images/investment-steps/acompanhar-fallback.webp",
"width": 240,
"height": 135,
"bytes": 5298,
"sha256": "cd4530cfef4f19f23f640ceae59cfd73d0ee269643118b458f2a4748f5845fc6"
}
]
}
],
"cards": [
{
"title": "1. Explorar",
"description": "Identifique o sector, território e oportunidade.",
"href": "/oportunidades",
"image": "explorar"
},
{
"title": "2. Preparar",
"description": "Conheça o percurso e organize os documentos.",
"href": "/como-investir",
"image": "preparar"
},
{
"title": "3. Acompanhar",
"description": "Envie o pedido e acompanhe o protocolo.",
"href": "/balcao-virtual",
"image": "acompanhar"
}
],
"presentation": {
"component": "resources/js/components/investment-steps.tsx",
"homeIntegration": "resources/js/pages/PortalInvestidor.tsx",
"aspectRatio": "16:9",
"responsiveImages": [
960,
480
],
"fallback": "Variante própria de 240 px; se também falhar, espaço reservado com descrição acessível e Imagem indisponível.",
"columns": "Uma coluna abaixo de 768 px, três a partir de 768 px.",
"compactText": "16 px de preenchimento, sem altura mínima de texto.",
"accessibility": "Alt em português, nomes e descrições das âncoras, foco visível, dimensões reservadas e loading lazy.",
"headings": "Título vermelho partilhado da home preservado."
},
"verification": {
"browser": {
"results": [
{
"width": 320,
"dpr": 1,
"fail": "none",
"columns": 1,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-steps/explorar-480.webp",
"alt": "Dois profissionais avaliam uma oportunidade de investimento no terreno com apoio de um tablet.",
"lazy": "lazy",
"ratio": 1.7820512820512822
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/preparar-480.webp",
"alt": "Empreendedora e consultor analisam documentos e organizam pastas numa reunião de preparação.",
"lazy": "lazy",
"ratio": 1.7820512820512822
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/acompanhar-480.webp",
"alt": "Empreendedor consulta um pedido no computador portátil numa secretária de escritório.",
"lazy": "lazy",
"ratio": 1.7820512820512822
}
],
"layoutShift": false,
"overflow": false
},
{
"width": 390,
"dpr": 2,
"fail": "none",
"columns": 1,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-steps/explorar.webp",
"alt": "Dois profissionais avaliam uma oportunidade de investimento no terreno com apoio de um tablet.",
"lazy": "lazy",
"ratio": 1.7755102040816326
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/preparar.webp",
"alt": "Empreendedora e consultor analisam documentos e organizam pastas numa reunião de preparação.",
"lazy": "lazy",
"ratio": 1.7755102040816326
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/acompanhar.webp",
"alt": "Empreendedor consulta um pedido no computador portátil numa secretária de escritório.",
"lazy": "lazy",
"ratio": 1.7755102040816326
}
],
"layoutShift": false,
"overflow": false
},
{
"width": 768,
"dpr": 1,
"fail": "none",
"columns": 3,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-steps/explorar-480.webp",
"alt": "Dois profissionais avaliam uma oportunidade de investimento no terreno com apoio de um tablet.",
"lazy": "lazy",
"ratio": 1.7734375
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/preparar-480.webp",
"alt": "Empreendedora e consultor analisam documentos e organizam pastas numa reunião de preparação.",
"lazy": "lazy",
"ratio": 1.7734375
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/acompanhar-480.webp",
"alt": "Empreendedor consulta um pedido no computador portátil numa secretária de escritório.",
"lazy": "lazy",
"ratio": 1.7734375
}
],
"layoutShift": false,
"overflow": false
},
{
"width": 1024,
"dpr": 1,
"fail": "none",
"columns": 3,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-steps/explorar-480.webp",
"alt": "Dois profissionais avaliam uma oportunidade de investimento no terreno com apoio de um tablet.",
"lazy": "lazy",
"ratio": 1.7784090909090908
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/preparar-480.webp",
"alt": "Empreendedora e consultor analisam documentos e organizam pastas numa reunião de preparação.",
"lazy": "lazy",
"ratio": 1.7784090909090908
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/acompanhar-480.webp",
"alt": "Empreendedor consulta um pedido no computador portátil numa secretária de escritório.",
"lazy": "lazy",
"ratio": 1.7784090909090908
}
],
"layoutShift": false,
"overflow": false
},
{
"width": 1440,
"dpr": 2,
"fail": "none",
"columns": 3,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-steps/explorar.webp",
"alt": "Dois profissionais avaliam uma oportunidade de investimento no terreno com apoio de um tablet.",
"lazy": "lazy",
"ratio": 1.775
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/preparar.webp",
"alt": "Empreendedora e consultor analisam documentos e organizam pastas numa reunião de preparação.",
"lazy": "lazy",
"ratio": 1.775
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/acompanhar.webp",
"alt": "Empreendedor consulta um pedido no computador portátil numa secretária de escritório.",
"lazy": "lazy",
"ratio": 1.775
}
],
"layoutShift": false,
"overflow": false
},
{
"width": 1920,
"dpr": 1,
"fail": "none",
"columns": 3,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-steps/explorar-480.webp",
"alt": "Dois profissionais avaliam uma oportunidade de investimento no terreno com apoio de um tablet.",
"lazy": "lazy",
"ratio": 1.775
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/preparar-480.webp",
"alt": "Empreendedora e consultor analisam documentos e organizam pastas numa reunião de preparação.",
"lazy": "lazy",
"ratio": 1.775
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/acompanhar-480.webp",
"alt": "Empreendedor consulta um pedido no computador portátil numa secretária de escritório.",
"lazy": "lazy",
"ratio": 1.775
}
],
"layoutShift": false,
"overflow": false
},
{
"width": 390,
"dpr": 2,
"fail": "primary",
"columns": 1,
"photos": [
{
"src": "http://127.0.0.1:5174/images/investment-steps/explorar-fallback.webp",
"alt": "Dois profissionais avaliam uma oportunidade de investimento no terreno com apoio de um tablet.",
"lazy": "lazy",
"ratio": 1.7755102040816326
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/preparar-fallback.webp",
"alt": "Empreendedora e consultor analisam documentos e organizam pastas numa reunião de preparação.",
"lazy": "lazy",
"ratio": 1.7755102040816326
},
{
"src": "http://127.0.0.1:5174/images/investment-steps/acompanhar-fallback.webp",
"alt": "Empreendedor consulta um pedido no computador portátil numa secretária de escritório.",
"lazy": "lazy",
"ratio": 1.7755102040816326
}
],
"layoutShift": false,
"overflow": false
},
{
"width": 390,
"dpr": 2,
"fail": "all",
"columns": 1,
"photos": [],
"layoutShift": false,
"overflow": false
}
],
"errors": [],
"navigation": [
"/oportunidades",
"/como-investir",
"/balcao-virtual"
],
"databaseAccess": false
},
"script": "outputs/investment-steps-20261023/check.mjs",
"screenshots": [
"outputs/investment-steps-20261023/cards-1024-none.png",
"outputs/investment-steps-20261023/cards-1440-none.png",
"outputs/investment-steps-20261023/cards-1920-none.png",
"outputs/investment-steps-20261023/cards-320-none.png",
"outputs/investment-steps-20261023/cards-390-all.png",
"outputs/investment-steps-20261023/cards-390-none.png",
"outputs/investment-steps-20261023/cards-390-primary.png",
"outputs/investment-steps-20261023/cards-768-none.png"
],
"lint": {
"exitCode": 0
},
"types": {
"command": "npm run types:check",
"exitCode": 0,
"log": "outputs/investment-steps-20261023/types.log"
},
"build": {
"command": "npm run build",
"exitCode": 0,
"homeAsset": "assets/PortalInvestidor-C1QTZ3-t.js",
"log": "outputs/investment-steps-20261023/build-local.log"
}
},
"scope": {
"appliedLocally": true,
"otherSectionsChanged": false,
"supportAndFooterChanged": false,
"databaseAccess": false,
"businessRulesChanged": false,
"routesChanged": false,
"newDependencies": false,
"externalDeployment": false
},
"matrixReferences": [
"MASTER-efd550fd-0104"
],
"limits": "Componentes reais com API sintética e destinos interceptados para verificar a navegação; não demonstra os workflows operacionais completos nem encerra o critério mais amplo da matriz."
}
@@ -0,0 +1,32 @@
{
"target": "https://teste-investidor.tempo.co.mz",
"authorization": "Tempo: rever e disponibilizar a entrega consolidada pelo processo Git existente; 23/10/2026",
"base": "e1f010f941e9993645e131b34ec1f4c3fe82c71b",
"continuity": "3d877ec7",
"qa_recovery_record": "115c7731",
"served_commit": "7c1f532134e4492dec010d0c511f669a1e2c23a7",
"finished_at": "2026-10-23T13:16:11.213670+00:00",
"status": "installed",
"release": "/var/www/investmoz-homologacao/releases/7c1f532134e4492dec010d0c511f669a1e2c23a7-524bd081",
"previous_release": "/var/www/investmoz-homologacao/releases/e1f010f941e9993645e131b34ec1f4c3fe82c71b-e905ae1f",
"manifest_sha256": "e72d2fc5343a7c3bb0f9c4b2f3f3c7660b3c6e2db5ba4995875c8130d021fd68",
"database_migrations": 0,
"content_imports": 0,
"permission_changes": 0,
"git": "Fast-forward integration; no remote-exclusive main changes; no force push or patch reapplication",
"initial_failure": "115c7731 preserved the active release: one report test detected omission of an associated inactive historical document. Corrected in 7c1f5321, retaining history without requiring the inapplicable document.",
"verification": {
"affected_local_tests": 17,
"types": "passed",
"automatic_frontend_tests": {"total": 204, "passed": 204, "failed": 0},
"served_revision": "Exact commit confirmed through public build/revision.json",
"public_home": "HTTP 200 without environmental HTTP authentication",
"portfolio": {"public_records": 29, "missing_approved_ids": 0, "changed_approved_titles": 0},
"assets": "Detail, context entry/editor and simulator assets HTTP 200",
"source_hashes": "Five affected server/report files match local Git release bytes",
"persistent_runtime": "Existing environment and storage remain symlinks; no replacement of data, uploads or secrets",
"private_workflow": "Accepted synthetic QA proofs reused; no real dossier writes or new destination business-flow test"
},
"rollback": "Existing pipeline rollback to preserved previous release; retain database, encrypted payloads, uploads, permissions and later history; no database restore",
"remaining": "Transversal adaptation of the rest of the portal is partial; absent portfolio associations are not inferred"
}
@@ -0,0 +1,34 @@
{
"functional_revision": "20349299",
"catalogue_url": "https://teste-investidor.tempo.co.mz/oportunidades?sector=agronegocio&view=corridors",
"homologation_browser_confirmed": true,
"public_opportunities": 34,
"groups": {
"Corredor de Pemba–Lichinga": 5,
"Corredor de Nacala": 6,
"Corredor do Vale do Zambeze": 6,
"Corredor da Beira": 7,
"Corredor do Limpopo": 5,
"Corredor de Maputo": 5
},
"browser_check": {
"selected_ids": [54, 64],
"corridors": ["Corredor da Beira", "Corredor de Maputo"],
"chain_filter": "Soja",
"filtered_beira_opportunities": 1,
"selection_retained_after_filter": true,
"existing_path": "/investimentos/iniciar?opportunities[]=54&opportunities[]=64",
"agribusiness_top_link_confirmed": true,
"screenshot": "outputs/preservation-inventory-20261023/agribusiness-corridor-explorer-homologation.png"
},
"qa_checks": {"tests": 2, "assertions": 26, "types": "passed", "build": "passed", "comparison_between_corridors": "confirmed in browser"},
"data_writes": false,
"migrations": 0,
"imports": 0,
"republication": false,
"limitations": [
"Province and district of implantation remain unidentified for these 34 records; no corridor boundary or administrative location is inferred.",
"Comparison reuses its existing capacity of four records; preparation can include more choices.",
"Production activities use the accepted source text rather than invented stage classifications."
]
}
@@ -0,0 +1,326 @@
{
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 174,
"changed_tables": {
"audit_events": {
"before": {
"rows": 520,
"checksum": "abeaf35ec4463ed7458f2e329a321edd"
},
"after": {
"rows": 622,
"checksum": "3518cc7405c62ad9be2f90da444fd3fe"
}
},
"cache": {
"before": {
"rows": 101,
"checksum": "6fdb8f22e7c00074aba9e7e2c7167ec0"
},
"after": {
"rows": 102,
"checksum": "d57b633a1293ed831a702d26f58f133c"
}
},
"opportunities": {
"before": {
"rows": 30,
"checksum": "8b55b9b474aa78562b40380698981fb6"
},
"after": {
"rows": 64,
"checksum": "6407d8192bb667003087a06aa3f0930f"
}
},
"sessions": {
"before": {
"rows": 24,
"checksum": "ee7deebc29696fe68a4bb1ab98c81215"
},
"after": {
"rows": 25,
"checksum": "0f41a35219cea6347427275b32897ad8"
}
}
},
"registered": 34,
"published": 34,
"records": [
{
"id": 31,
"title": "Batata, trigo, feijão, milho e soja — Corredor de Pemba–Lichinga",
"slug": "inv-mz-gen-7676707848914068",
"key": "80858ef1-42ad-5bc6-88ee-0a4a6253f9e2",
"status": "published"
},
{
"id": 32,
"title": "Algodão — Corredor de Pemba–Lichinga",
"slug": "inv-mz-gen-9477691804857755",
"key": "29990fe3-2453-5308-8a23-27622209ab02",
"status": "published"
},
{
"id": 33,
"title": "Sésamo — Corredor de Pemba–Lichinga",
"slug": "inv-mz-gen-7401258130600878",
"key": "9e79c4ac-01d5-5d20-abf3-b49df6ef8206",
"status": "published"
},
{
"id": 36,
"title": "Mandioca — Corredor de Nacala",
"slug": "inv-mz-gen-1439048955084540",
"key": "4be5f275-3504-5a7e-8ba9-d51a51efd534",
"status": "published"
},
{
"id": 38,
"title": "Algodão — Corredor de Nacala",
"slug": "inv-mz-gen-9644546880756388",
"key": "b337b1cf-01ae-5c35-9949-90704ea68f0f",
"status": "published"
},
{
"id": 39,
"title": "Frutas e caju — Corredor de Nacala",
"slug": "inv-mz-gen-2468979899503991",
"key": "1e9d0e7a-9c63-58cb-9951-349c9cc236c6",
"status": "published"
},
{
"id": 40,
"title": "Florestas — Corredor de Nacala",
"slug": "inv-mz-gen-7039993456771493",
"key": "ebd761cb-e793-5d11-809a-dc04e4baeb9d",
"status": "published"
},
{
"id": 41,
"title": "Avicultura — Corredor de Nacala",
"slug": "inv-mz-gen-5080757788800956",
"key": "69d7e89a-bd72-574c-8ce1-104f4b8448d4",
"status": "published"
},
{
"id": 42,
"title": "Arroz — Corredor do Vale do Zambeze",
"slug": "inv-mz-gen-5601915696649995",
"key": "405f0e8a-973f-5a2f-bbe8-f923944bcde1",
"status": "published"
},
{
"id": 43,
"title": "Milho e batata — Corredor do Vale do Zambeze",
"slug": "inv-mz-gen-2709717524887763",
"key": "7002d77b-2df6-5373-91b0-a67abed64378",
"status": "published"
},
{
"id": 44,
"title": "Sésamo — Corredor do Vale do Zambeze",
"slug": "inv-mz-gen-9164411293252209",
"key": "4bfe5b57-ea81-564e-a6ec-8867de5b79f7",
"status": "published"
},
{
"id": 45,
"title": "Algodão — Corredor do Vale do Zambeze",
"slug": "inv-mz-gen-3014063241101625",
"key": "40c73a69-8724-5552-9612-ecd9ec50d6d8",
"status": "published"
},
{
"id": 46,
"title": "Avicultura — Corredor do Vale do Zambeze",
"slug": "inv-mz-gen-6214131825033367",
"key": "d8ab3e05-a180-5a0a-877e-c9b4fab34931",
"status": "published"
},
{
"id": 48,
"title": "Milho, trigo e arroz — Corredor da Beira",
"slug": "inv-mz-gen-6048328347519124",
"key": "a80dc6b8-1c9b-53ef-bf31-ec37fa22561f",
"status": "published"
},
{
"id": 49,
"title": "Soja — Corredor da Beira",
"slug": "inv-mz-gen-4587508237495307",
"key": "59726827-4fe9-5256-af12-10bd56dab0e3",
"status": "published"
},
{
"id": 52,
"title": "Açúcar — Corredor da Beira",
"slug": "inv-mz-gen-1634854290375763",
"key": "bc1ee2df-33f9-5262-9ad4-1af961393c88",
"status": "published"
},
{
"id": 53,
"title": "Florestas — Corredor da Beira",
"slug": "inv-mz-gen-8326396601945337",
"key": "aaf5661e-0a45-561a-b74e-c5cbfaad7721",
"status": "published"
},
{
"id": 55,
"title": "Arroz — Corredor do Limpopo",
"slug": "inv-mz-gen-4222404773242970",
"key": "0ebbae53-7331-5360-b704-3060f2f52548",
"status": "published"
},
{
"id": 37,
"title": "Milho, amendoim e sésamo — Corredor de Nacala",
"slug": "inv-mz-gen-5107565297512781",
"key": "7887e378-6d59-5acc-94e1-41f320e690f6",
"status": "published"
},
{
"id": 56,
"title": "Hortícolas — Corredor do Limpopo",
"slug": "inv-mz-gen-2333485246312757",
"key": "5ead8334-a1ea-5710-b5ae-751e1a97bc18",
"status": "published"
},
{
"id": 58,
"title": "Avicultura — Corredor do Limpopo",
"slug": "inv-mz-gen-9065431632626708",
"key": "7b4070c4-59f9-59f3-ae3a-ef897998e5ae",
"status": "published"
},
{
"id": 59,
"title": "Infraestruturas e serviços de apoio — Corredor do Limpopo",
"slug": "inv-mz-gen-5245892626712728",
"key": "09fa3da3-96c0-562c-8b3d-1010841fac16",
"status": "published"
},
{
"id": 60,
"title": "Arroz — Corredor de Maputo",
"slug": "inv-mz-gen-8724580943323815",
"key": "8b3cd51b-d51a-5a60-9ec3-9222db943bbb",
"status": "published"
},
{
"id": 63,
"title": "Avicultura — Corredor de Maputo",
"slug": "inv-mz-gen-4404538462410084",
"key": "a15d68d3-02c8-50d9-a03b-63f0478733ff",
"status": "published"
},
{
"id": 34,
"title": "Florestas — Corredor de Pemba–Lichinga",
"slug": "inv-mz-gen-2816022300638626",
"key": "ccde9a38-0a27-5b46-a158-b2013128baaf",
"status": "published"
},
{
"id": 35,
"title": "Avicultura — Corredor de Pemba–Lichinga",
"slug": "inv-mz-gen-6844264076753489",
"key": "1748aee6-cf7a-5e16-acde-d37777578d3a",
"status": "published"
},
{
"id": 47,
"title": "Bovinos e caprinos — Corredor do Vale do Zambeze",
"slug": "inv-mz-gen-5894491385867554",
"key": "8a493e37-5d26-5cc7-9937-aab3e7ad0f40",
"status": "published"
},
{
"id": 50,
"title": "Hortícolas — Corredor da Beira",
"slug": "inv-mz-gen-3954947238505605",
"key": "2d0fb383-6d02-5df6-a24a-57474ab15ad0",
"status": "published"
},
{
"id": 51,
"title": "Sésamo — Corredor da Beira",
"slug": "inv-mz-gen-8142311444413640",
"key": "791e2921-92cf-5e08-9ff7-99a275bd6f34",
"status": "published"
},
{
"id": 54,
"title": "Carne bovina — Corredor da Beira",
"slug": "inv-mz-gen-3963957640266320",
"key": "45423b00-4673-5595-b3ac-cc4ab8e1dc8a",
"status": "published"
},
{
"id": 57,
"title": "Carne vermelha — Corredor do Limpopo",
"slug": "inv-mz-gen-4521723858186670",
"key": "fbbb0961-e5b0-5900-ad33-4efb5fc7bfc5",
"status": "published"
},
{
"id": 61,
"title": "Hortícolas — Corredor de Maputo",
"slug": "inv-mz-gen-3403417739078882",
"key": "14b76956-6afa-5c52-8d56-db402dbbd099",
"status": "published"
},
{
"id": 62,
"title": "Carne vermelha — Corredor de Maputo",
"slug": "inv-mz-gen-6923224180891809",
"key": "bd07739d-e0b8-5561-8baa-14f67571ecf5",
"status": "published"
},
{
"id": 64,
"title": "Infraestruturas e serviços de apoio — Corredor de Maputo",
"slug": "inv-mz-gen-7371026296970534",
"key": "eb5bad23-5ea2-5f81-bb2e-85609853ae1b",
"status": "published"
}
],
"resources": 59,
"geometries": 408,
"preexisting_opportunities_preserved": true,
"comparison_reader": "postgres, same identity as backup; RLS-filtered application counts are not used as loss evidence",
"served_revision": {
"commit": "e12ef6ad426ff6ebc4e7421a1ddbe7f184d77a55",
"branch": "main",
"built_at": "2026-10-23T09:01:54.733542+00:00",
"manifest_sha256": "a7fb08d79db71f19f0dde06c8ebb9329cfaf8e93c2f5a80f460aef98aa885398"
},
"installation_complete": true,
"registration_complete": true,
"publication_complete": true,
"pending": null,
"backup_restore": {
"verified": true,
"tables": 174,
"files": 369,
"dump_sha256": "318fcdce6623f1032fa319372a08ff4219f4def509f64cf73b39674e8b85260e",
"files_sha256": "b0903725916a9b523a0f90c10ae6c42877f4374fb711f900f19e0b2053606536"
},
"management_url": "https://teste-investidor.tempo.co.mz/minhas-oportunidades/lotes/agronegocio",
"comparison_correction": "Initial application read was RLS-filtered; equivalent postgres read confirmed all simulation records preserved. No data restoration or security policy changes executed.",
"execution": "Ordinary authenticated real-account browser interface, preview and confirmation, existing recent MFA. No console registration or publication.",
"public_url": "https://teste-investidor.tempo.co.mz/oportunidades?sector=agronegocio",
"browser_proof": {
"results": 34,
"selected_ids": [
64,
63
],
"path": "/investimentos/iniciar?opportunities[]=64&opportunities[]=63",
"screenshot": "outputs/preservation-inventory-20261023/agribusiness-homologation-published.png"
},
"unchanged_tables": 170
}
@@ -0,0 +1,214 @@
{
"target": {
"name": "simulator_fiscal_delta_20261021_test",
"port": 55441
},
"source_sha256": "1473b06c48aa234e771660e3f2a51b9d2e4e1943bd52d878e0ae854ee6657055",
"records": [
{
"id": 269,
"created": true,
"key": "80858ef1-42ad-5bc6-88ee-0a4a6253f9e2"
},
{
"id": 270,
"created": true,
"key": "29990fe3-2453-5308-8a23-27622209ab02"
},
{
"id": 271,
"created": true,
"key": "9e79c4ac-01d5-5d20-abf3-b49df6ef8206"
},
{
"id": 272,
"created": true,
"key": "ccde9a38-0a27-5b46-a158-b2013128baaf"
},
{
"id": 273,
"created": true,
"key": "1748aee6-cf7a-5e16-acde-d37777578d3a"
},
{
"id": 274,
"created": true,
"key": "4be5f275-3504-5a7e-8ba9-d51a51efd534"
},
{
"id": 275,
"created": true,
"key": "7887e378-6d59-5acc-94e1-41f320e690f6"
},
{
"id": 276,
"created": true,
"key": "b337b1cf-01ae-5c35-9949-90704ea68f0f"
},
{
"id": 277,
"created": true,
"key": "1e9d0e7a-9c63-58cb-9951-349c9cc236c6"
},
{
"id": 278,
"created": true,
"key": "ebd761cb-e793-5d11-809a-dc04e4baeb9d"
},
{
"id": 279,
"created": true,
"key": "69d7e89a-bd72-574c-8ce1-104f4b8448d4"
},
{
"id": 280,
"created": true,
"key": "405f0e8a-973f-5a2f-bbe8-f923944bcde1"
},
{
"id": 281,
"created": true,
"key": "7002d77b-2df6-5373-91b0-a67abed64378"
},
{
"id": 282,
"created": true,
"key": "4bfe5b57-ea81-564e-a6ec-8867de5b79f7"
},
{
"id": 283,
"created": true,
"key": "40c73a69-8724-5552-9612-ecd9ec50d6d8"
},
{
"id": 284,
"created": true,
"key": "d8ab3e05-a180-5a0a-877e-c9b4fab34931"
},
{
"id": 285,
"created": true,
"key": "8a493e37-5d26-5cc7-9937-aab3e7ad0f40"
},
{
"id": 286,
"created": true,
"key": "a80dc6b8-1c9b-53ef-bf31-ec37fa22561f"
},
{
"id": 287,
"created": true,
"key": "59726827-4fe9-5256-af12-10bd56dab0e3"
},
{
"id": 288,
"created": true,
"key": "2d0fb383-6d02-5df6-a24a-57474ab15ad0"
},
{
"id": 289,
"created": true,
"key": "791e2921-92cf-5e08-9ff7-99a275bd6f34"
},
{
"id": 290,
"created": true,
"key": "bc1ee2df-33f9-5262-9ad4-1af961393c88"
},
{
"id": 291,
"created": true,
"key": "aaf5661e-0a45-561a-b74e-c5cbfaad7721"
},
{
"id": 292,
"created": true,
"key": "45423b00-4673-5595-b3ac-cc4ab8e1dc8a"
},
{
"id": 293,
"created": true,
"key": "0ebbae53-7331-5360-b704-3060f2f52548"
},
{
"id": 294,
"created": true,
"key": "5ead8334-a1ea-5710-b5ae-751e1a97bc18"
},
{
"id": 295,
"created": true,
"key": "fbbb0961-e5b0-5900-ad33-4efb5fc7bfc5"
},
{
"id": 296,
"created": true,
"key": "7b4070c4-59f9-59f3-ae3a-ef897998e5ae"
},
{
"id": 297,
"created": true,
"key": "09fa3da3-96c0-562c-8b3d-1010841fac16"
},
{
"id": 298,
"created": true,
"key": "8b3cd51b-d51a-5a60-9ec3-9222db943bbb"
},
{
"id": 299,
"created": true,
"key": "14b76956-6afa-5c52-8d56-db402dbbd099"
},
{
"id": 300,
"created": true,
"key": "bd07739d-e0b8-5561-8baa-14f67571ecf5"
},
{
"id": 301,
"created": true,
"key": "a15d68d3-02c8-50d9-a03b-63f0478733ff"
},
{
"id": 302,
"created": true,
"key": "eb5bad23-5ea2-5f81-bb2e-85609853ae1b"
}
],
"total": 34,
"new": 34,
"published": 0,
"active_writes": 0,
"status": "QA private registration complete; homologation unchanged",
"preservation": {
"target": {
"name": "simulator_fiscal_delta_20261021_test",
"port": 55441
},
"records": 34,
"private_drafts": 34,
"preserved": {
"opportunities": true,
"audit_events": true,
"development_corridors": true,
"portal_role_assignments": true,
"portal_resources": true,
"documents": true
},
"active_writes": 0
},
"checks": {
"feature_tests": 3,
"assertions": 553,
"types": "passed",
"frontend_build": "passed",
"test_disclosures_rolled_back": true
},
"management_url": "http://127.0.0.1:18574/gestao-conteudos",
"individual_editor_example": "http://127.0.0.1:18574/minhas-oportunidades/269/edit",
"reused_promotional_candidate": "9d3bfa89",
"schema_changes": 0,
"new_roles": 0,
"new_dependencies": 0
}
@@ -0,0 +1,246 @@
{
"target": {
"name": "simulator_fiscal_delta_20261021_test",
"port": 55441
},
"published": 34,
"authentication": "QA role simulation only; no production authentication claimed",
"source_sha256": "1473b06c48aa234e771660e3f2a51b9d2e4e1943bd52d878e0ae854ee6657055",
"records": [
{
"id": 269,
"title": "Batata, trigo, feijão, milho e soja — Corredor de Pemba–Lichinga",
"corridor": "Corredor de Pemba–Lichinga",
"url": "/oportunidades/inv-mz-gen-5150397406444731"
},
{
"id": 270,
"title": "Algodão — Corredor de Pemba–Lichinga",
"corridor": "Corredor de Pemba–Lichinga",
"url": "/oportunidades/inv-mz-gen-1647249176453799"
},
{
"id": 271,
"title": "Sésamo — Corredor de Pemba–Lichinga",
"corridor": "Corredor de Pemba–Lichinga",
"url": "/oportunidades/inv-mz-gen-3352774267583285"
},
{
"id": 272,
"title": "Florestas — Corredor de Pemba–Lichinga",
"corridor": "Corredor de Pemba–Lichinga",
"url": "/oportunidades/inv-mz-gen-7850139886806206"
},
{
"id": 273,
"title": "Avicultura — Corredor de Pemba–Lichinga",
"corridor": "Corredor de Pemba–Lichinga",
"url": "/oportunidades/inv-mz-gen-3839727014705762"
},
{
"id": 274,
"title": "Mandioca — Corredor de Nacala",
"corridor": "Corredor de Nacala",
"url": "/oportunidades/inv-mz-gen-8963722873273976"
},
{
"id": 275,
"title": "Milho, amendoim e sésamo — Corredor de Nacala",
"corridor": "Corredor de Nacala",
"url": "/oportunidades/inv-mz-gen-9998208319499345"
},
{
"id": 276,
"title": "Algodão — Corredor de Nacala",
"corridor": "Corredor de Nacala",
"url": "/oportunidades/inv-mz-gen-6731688736856562"
},
{
"id": 277,
"title": "Frutas e caju — Corredor de Nacala",
"corridor": "Corredor de Nacala",
"url": "/oportunidades/inv-mz-gen-2325832878312017"
},
{
"id": 278,
"title": "Florestas — Corredor de Nacala",
"corridor": "Corredor de Nacala",
"url": "/oportunidades/inv-mz-gen-8943819075565910"
},
{
"id": 279,
"title": "Avicultura — Corredor de Nacala",
"corridor": "Corredor de Nacala",
"url": "/oportunidades/inv-mz-gen-3960434222994703"
},
{
"id": 280,
"title": "Arroz — Corredor do Vale do Zambeze",
"corridor": "Corredor do Vale do Zambeze",
"url": "/oportunidades/inv-mz-gen-4676568685660682"
},
{
"id": 281,
"title": "Milho e batata — Corredor do Vale do Zambeze",
"corridor": "Corredor do Vale do Zambeze",
"url": "/oportunidades/inv-mz-gen-9955994100950689"
},
{
"id": 282,
"title": "Sésamo — Corredor do Vale do Zambeze",
"corridor": "Corredor do Vale do Zambeze",
"url": "/oportunidades/inv-mz-gen-5272341950046110"
},
{
"id": 283,
"title": "Algodão — Corredor do Vale do Zambeze",
"corridor": "Corredor do Vale do Zambeze",
"url": "/oportunidades/inv-mz-gen-5628419400094441"
},
{
"id": 284,
"title": "Avicultura — Corredor do Vale do Zambeze",
"corridor": "Corredor do Vale do Zambeze",
"url": "/oportunidades/inv-mz-gen-8668117557933061"
},
{
"id": 285,
"title": "Bovinos e caprinos — Corredor do Vale do Zambeze",
"corridor": "Corredor do Vale do Zambeze",
"url": "/oportunidades/inv-mz-gen-6651453468338687"
},
{
"id": 286,
"title": "Milho, trigo e arroz — Corredor da Beira",
"corridor": "Corredor da Beira",
"url": "/oportunidades/inv-mz-gen-8128691124566423"
},
{
"id": 287,
"title": "Soja — Corredor da Beira",
"corridor": "Corredor da Beira",
"url": "/oportunidades/inv-mz-gen-5563232446855414"
},
{
"id": 288,
"title": "Hortícolas — Corredor da Beira",
"corridor": "Corredor da Beira",
"url": "/oportunidades/inv-mz-gen-5715109945209502"
},
{
"id": 289,
"title": "Sésamo — Corredor da Beira",
"corridor": "Corredor da Beira",
"url": "/oportunidades/inv-mz-gen-6307033318408482"
},
{
"id": 290,
"title": "Açúcar — Corredor da Beira",
"corridor": "Corredor da Beira",
"url": "/oportunidades/inv-mz-gen-8123001467176624"
},
{
"id": 291,
"title": "Florestas — Corredor da Beira",
"corridor": "Corredor da Beira",
"url": "/oportunidades/inv-mz-gen-5629890674429876"
},
{
"id": 292,
"title": "Carne bovina — Corredor da Beira",
"corridor": "Corredor da Beira",
"url": "/oportunidades/inv-mz-gen-2472671405287788"
},
{
"id": 293,
"title": "Arroz — Corredor do Limpopo",
"corridor": "Corredor do Limpopo",
"url": "/oportunidades/inv-mz-gen-5995177607070005"
},
{
"id": 294,
"title": "Hortícolas — Corredor do Limpopo",
"corridor": "Corredor do Limpopo",
"url": "/oportunidades/inv-mz-gen-7967808283045554"
},
{
"id": 295,
"title": "Carne vermelha — Corredor do Limpopo",
"corridor": "Corredor do Limpopo",
"url": "/oportunidades/inv-mz-gen-5036463681202017"
},
{
"id": 296,
"title": "Avicultura — Corredor do Limpopo",
"corridor": "Corredor do Limpopo",
"url": "/oportunidades/inv-mz-gen-5524442722730781"
},
{
"id": 297,
"title": "Infraestruturas e serviços de apoio — Corredor do Limpopo",
"corridor": "Corredor do Limpopo",
"url": "/oportunidades/inv-mz-gen-2099729870926704"
},
{
"id": 298,
"title": "Arroz — Corredor de Maputo",
"corridor": "Corredor de Maputo",
"url": "/oportunidades/inv-mz-gen-1866403697817634"
},
{
"id": 299,
"title": "Hortícolas — Corredor de Maputo",
"corridor": "Corredor de Maputo",
"url": "/oportunidades/inv-mz-gen-6293243388893029"
},
{
"id": 300,
"title": "Carne vermelha — Corredor de Maputo",
"corridor": "Corredor de Maputo",
"url": "/oportunidades/inv-mz-gen-9330026487459372"
},
{
"id": 301,
"title": "Avicultura — Corredor de Maputo",
"corridor": "Corredor de Maputo",
"url": "/oportunidades/inv-mz-gen-1761515912486377"
},
{
"id": 302,
"title": "Infraestruturas e serviços de apoio — Corredor de Maputo",
"corridor": "Corredor de Maputo",
"url": "/oportunidades/inv-mz-gen-2643789930780297"
}
],
"active_writes": 0,
"checks": {
"context_tests": 7,
"context_assertions": 132,
"batch_tests": 1,
"batch_assertions": 15,
"frontend_types": "passed",
"frontend_build": "passed",
"browser_catalogue_results": 34,
"browser_selected_opportunities": 2
},
"preservation": {
"target": {
"name": "simulator_fiscal_delta_20261021_test",
"port": 55441
},
"records": 34,
"private_drafts": 0,
"preserved": {
"opportunities": true,
"audit_events": true,
"development_corridors": true,
"portal_role_assignments": true,
"portal_resources": true,
"documents": true
},
"active_writes": 0,
"published_in_qa": 34
},
"qa_catalogue_url": "http://127.0.0.1:18574/oportunidades?sector=agronegocio",
"homologation_status": "Backup/restore verified; code delivery and authenticated registration/disclosure still separate."
}
@@ -0,0 +1,36 @@
{
"url": "https://teste-investidor.tempo.co.mz/agronegocio",
"status": 200,
"served_revision": "20a4099d10400496e19f03589b6383f512b48d9d",
"sections": [
{
"name": "Corredor de Pemba–Lichinga",
"entries": 5
},
{
"name": "Corredor de Nacala",
"entries": 6
},
{
"name": "Corredor do Vale do Zambeze",
"entries": 6
},
{
"name": "Corredor da Beira",
"entries": 7
},
{
"name": "Corredor do Limpopo",
"entries": 5
},
{
"name": "Corredor de Maputo",
"entries": 5
}
],
"entries": 34,
"original_html_preserved": true,
"metadata_image_version_source_preserved": true,
"article_sha256": "0929184a6d4645b9046512675394ad4f60575273f6dab0955f71cc7474a6a4e2",
"database_writes": 0
}
@@ -0,0 +1,333 @@
{
"status": "installed_in_homologation_territories_recovered_delta_verified",
"date": "2026-10-23",
"base": "8036b9b0",
"accepted_delivery_receipt": "cdf8fcfb",
"active_comparison": {
"database": {
"database": "teste_investidor",
"port": 5432,
"role": "teste_investidor_app"
},
"read_only": "on",
"published_fiches": 29,
"cluster_relations": 13,
"project_locations": 0,
"RLS": [
{
"relname": "projects",
"relrowsecurity": false,
"relforcerowsecurity": false
},
{
"relname": "project_locations",
"relrowsecurity": false,
"relforcerowsecurity": false
},
{
"relname": "cluster_projects",
"relrowsecurity": false,
"relforcerowsecurity": false
},
{
"relname": "investment_dossiers",
"relrowsecurity": false,
"relforcerowsecurity": false
},
{
"relname": "opportunity_projects",
"relrowsecurity": false,
"relforcerowsecurity": false
}
],
"missing_accepted_locations": 28,
"source_inventory": "inventory-20261007-172113.json",
"cause_scope": "Transported project identities omit accepted territorial rows and citation metadata; visibility recovery only restored publication status. Exact event/time not established."
},
"qa_recovery": {
"mode": "applied",
"database": "simulator_fiscal_delta_20261021_test",
"port": 55441,
"qa": true,
"manifest_sha256": "dc3ae2014da51005d95780e2e4ae87e55e000e66e0765f0671c6e44a958026a0",
"preview_sha256": "601a8603cf39f51a22cef9195e238a21c1552cb3303672b78ec52826c6342b57",
"relations": 28,
"projects": 13,
"relations_restored": 28,
"projects_updated": 13,
"new_dossiers": 0,
"publication_changes": 0
},
"tests": {
"php": {
"tests": 4,
"assertions": 116,
"passed": 4
},
"frontend": {
"tests": 2,
"passed": 2
},
"typescript": "passed",
"build": "passed",
"phpstan_new_recovery_service": "passed, zero errors"
},
"backup": {
"stage": "/var/backups/investmoz-anchor-integration-20261023/backup",
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 174,
"files_verified": 369,
"dump_sha256": "207ff14c6af7c4b2dd280def898945cb58314e01930b51c50d502e95cbe6ac68",
"files_sha256": "8cd85ab7d5a772452111e4fe7feab48f7fc694ee3f0fbd264f50096254835fe2",
"restore_verified": false
},
"isolated_restore": {
"restore_verified": true,
"tables_verified": 174,
"container_network": "none",
"database": "anchor_restore_20261023",
"port": 55441,
"source_dump_sha256": "207ff14c6af7c4b2dd280def898945cb58314e01930b51c50d502e95cbe6ac68",
"active_database_writes": 0
},
"preserved_closures": [
"6b544548",
"d8224ba8",
"55136b44",
"8036b9b0"
],
"not_repeated": [
"native saved PDF",
"Word",
"ZIP",
"accepted portfolio via opportunity branch",
"accepted complements and four consumers"
],
"deferred": [
"statistics",
"AI"
],
"pending": [],
"screenshots": [
"outputs/anchor-integration-20261023/qa-sector-location.png",
"outputs/anchor-integration-20261023/qa-anchor-fiche.png",
"outputs/anchor-integration-20261023/qa-anchor-dossier.png",
"outputs/anchor-integration-20261023/qa-anchor-simulator.png",
"outputs/anchor-integration-20261023/homologation-sector-location.png",
"outputs/anchor-integration-20261023/homologation-anchor-fiche.png"
],
"visual_QA": {
"anchor_code": "A06",
"anchor_fiche": "57a4ee7a-4c07-4e26-b8f5-e07726e41512",
"case_id": "3fd1bac6-80cf-48c8-97f3-eb4bb3398a17",
"simulation_draft_id": "bbc9ae3d-8c0e-45d2-b93f-9eaa85bab4d4",
"sector": "Agronegócio",
"province": "Sofala",
"offer_invented": false,
"deliberate_title_preserved": true,
"source_link_visible_in_dossier_and_simulator": true,
"manifestation": "HTTP directed proof, including ownership, same-source identity and versioned binding; browser native confirmation cancelled after control timeout; no formal submission"
},
"served_commit": "cf2ddf21a1457f4852fe6aa10069ec3b854f1567",
"functional_commits": [
"3fd5128c",
"cf2ddf21"
],
"installation": {
"service": "ExecMainStatus=0\nActiveState=inactive\nSubState=dead\n",
"installed": {
"status": "installed",
"commit": "cf2ddf21a1457f4852fe6aa10069ec3b854f1567",
"release": "/var/www/investmoz-homologacao/releases/cf2ddf21a1457f4852fe6aa10069ec3b854f1567-7749f82e",
"previous_release": "/var/www/investmoz-homologacao/releases/3fd5128c62296bc5c85dcd19f1458a7949de5784-803cd41e",
"database_migrations": 0,
"content_imports": 0
},
"latest_report": {
"status": "installed",
"commit": "cf2ddf21a1457f4852fe6aa10069ec3b854f1567",
"release": "/var/www/investmoz-homologacao/releases/cf2ddf21a1457f4852fe6aa10069ec3b854f1567-7749f82e",
"previous_release": "/var/www/investmoz-homologacao/releases/3fd5128c62296bc5c85dcd19f1458a7949de5784-803cd41e",
"database_migrations": 0,
"content_imports": 0,
"requested_commits": [
"cf2ddf21a1457f4852fe6aa10069ec3b854f1567"
],
"finished_at": "2026-10-23T17:21:57.897758+00:00"
},
"log_file": "1792776037181186915.log",
"summaries": [
"OK (1 test, 3106 assertions)",
"built in 24.44s",
"OK (57 tests, 309 assertions)",
"# tests 216",
"# pass 216",
"# fail 0"
],
"log_bytes": 92267
},
"recovery_preview": {
"mode": "preview",
"database": "teste_investidor",
"port": 5432,
"qa": false,
"manifest_sha256": "d2893d0a638d630435aca1890f79a8094dc58eb65bbdd65a470bb685fa075190",
"preview_sha256": "f59e324222ea480d176f1e8e6ae164efa184904c8244873d3fcf6af8497e0dac",
"relations": 28,
"projects": 13
},
"recovery_applied": {
"mode": "applied",
"database": "teste_investidor",
"port": 5432,
"qa": false,
"manifest_sha256": "d2893d0a638d630435aca1890f79a8094dc58eb65bbdd65a470bb685fa075190",
"preview_sha256": "f59e324222ea480d176f1e8e6ae164efa184904c8244873d3fcf6af8497e0dac",
"relations": 28,
"projects": 13,
"relations_restored": 28,
"projects_updated": 13,
"new_dossiers": 0,
"publication_changes": 0
},
"installed_delta": {
"published_fiches": 29,
"territorial_links": 28,
"affected_projects": 13,
"cluster_relations": 13,
"dossier_rows_unchanged": true,
"clusters_corridors_opportunity_relations_unchanged": true,
"historical_territorial_link_identities_preserved": true,
"only_project_metadata_version_timestamp_changed": true,
"functional_file_hashes_match_Git": {
"app/Http/Controllers/InvestmentPortfolioController.php": true,
"app/Http/Controllers/InvestorContextController.php": true,
"app/Services/ApprovedAnchorTerritories.php": true,
"app/Services/InvestorContext.php": true,
"resources/js/lib/investor-context.ts": true,
"resources/js/lib/project-dossier-report.ts": true,
"resources/js/lib/search-territory.ts": true,
"resources/js/pages/investment/submission.tsx": true,
"resources/js/pages/investor-path/context-start.tsx": true,
"resources/js/pages/investor-path/context.tsx": true,
"resources/js/pages/investor-path/show.tsx": true,
"resources/js/pages/portfolio/index.tsx": true,
"resources/js/pages/portfolio/show.tsx": true,
"routes/web.php": true,
"tools/restore-approved-anchor-territories.php": true
},
"pipeline_summaries": [
"OK (1 test, 3106 assertions)",
"built in 24.44s",
"OK (57 tests, 309 assertions)",
"# tests 216",
"# pass 216",
"# fail 0"
],
"table_preservation": {
"tables": 174,
"changed_tables": {
"audit_events": {
"before": {
"rows": 632,
"checksum": "d4f2e8b30c014080b33a588eb0d8afe2"
},
"after": {
"rows": 645,
"checksum": "88c2047620eb3fad4d2a2837ed3daef5"
}
},
"cache": {
"before": {
"rows": 104,
"checksum": "463aff806806b9fa2e4dff3310f516ec"
},
"after": {
"rows": 104,
"checksum": "4963d5a25fecf3352cf0d7aee73a1e63"
}
},
"project_locations": {
"before": {
"rows": 0,
"checksum": "d41d8cd98f00b204e9800998ecf8427e"
},
"after": {
"rows": 28,
"checksum": "80b4e44b11208a023fcfb989e738b95a"
}
},
"projects": {
"before": {
"rows": 16,
"checksum": "af127d727cfc1050c26b99130e397109"
},
"after": {
"rows": 16,
"checksum": "17fb48a69b09e1e8ec81b0d30e29ce80"
}
},
"sessions": {
"before": {
"rows": 11,
"checksum": "912e728cc332bfc41a7e0f94c84e118d"
},
"after": {
"rows": 15,
"checksum": "713242c22c549258fab61d1b9cd993ef"
}
}
},
"unchanged_tables": 169
},
"exact_29_approved_fiche_identities_preserved": true,
"synthetic_fiches_transported": 0
},
"Git_preservation": {
"accepted_base_receipt": "cdf8fcfb",
"previous_installed": "8036b9b0",
"candidate": "cf2ddf21a1457f4852fe6aa10069ec3b854f1567",
"protected_files_unchanged": {
"app/Services/InvestmentComplements.php": true,
"app/Services/InvestmentConsumerScope.php": true,
"app/Services/InvestorPath.php": true,
"app/Services/SimulationFormDrafts.php": true,
"resources/js/components/investment-complements.tsx": true,
"resources/js/components/dossier-prefill.tsx": true,
"resources/js/components/investment-requirements.tsx": true
},
"matrix_changed_rows_since_accepted_receipt": [
"AUD-REL-02",
"AUD-LEG-06"
],
"other_matrix_records_preserved": 3421,
"accepted_receipt_updates_preserved": [
"AUD-SIM-INT03",
"AUD-SIM-INT06"
],
"schema_migrations_added": [],
"stats_AI_code_changed": false,
"force_push": false
},
"manifest_verification": {
"Git_LF_sha256": "d2893d0a638d630435aca1890f79a8094dc58eb65bbdd65a470bb685fa075190",
"initial_QA_Windows_bytes_sha256": "dc3ae2014da51005d95780e2e4ae87e55e000e66e0765f0671c6e44a958026a0",
"canonicalization": "CRLF to LF only; identical JSON and accepted relations",
"first_active_preview": "refused before writes; corrected canonical preview and apply succeeded"
},
"next": "Tempo final navigation and presentation evaluation",
"not_a_claim_of_complete_transversal_implementation": true,
"installed_public_delta": {
"catalogue": "https://teste-investidor.tempo.co.mz/carteira?kind=project&sector=Agro-ind%C3%BAstria%20e%20agricultura&province_id=f57ff041-6275-57b9-8800-c35e46e0d475",
"results": 1,
"fiche": "57a4ee7a-4c07-4e26-b8f5-e07726e41512",
"source": "portfolio:57a4ee7a-4c07-4e26-b8f5-e07726e41512",
"territories_visible": 4,
"search_return_preserves_filters": true,
"preparation_GET_same_identity_and_province": true,
"homologation_test_dossiers_or_submissions_created": 0
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,358 @@
{
"format": "investmoz-approved-portfolio-visibility-recovery-v1",
"request": "Tempo, 23/10/2026: inventariar funcionalidades concluídas, inventariar desaparecidas e repor, preservando o aprovado",
"scope": "Restore visibility of exact unchanged previously owner-approved dossiers, not approve new or modified content",
"source_database": "investemoz",
"source_snapshot_sha256": "c27a34e30617ce56164daab0aea5cdd7e43b8bec9b2fa197ef616e6c87e70575",
"historic_snapshot_sha256": "6904486ee9c3895ee5e42d9ef19e6cb5f223930f7734a7af18051053d019cda6",
"items": [
{
"code": "A01",
"title": "MozParks – 20 Parques Industriais",
"source_record_id": "219e1dbb-939c-4ef0-92b7-7f78f8561fa0",
"target_record_id": "9871a8c0-d562-420b-a8f7-174c95db93f0",
"expected_target_version": 1,
"content_sha256": "2adcbe741777d4352c47eb31281dc6b2e2d04903fcfd3e59778ddff4e1342977",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A02",
"title": "Linha Ferroviária Norte–Sul",
"source_record_id": "1125f069-61e2-4071-99f3-dd103caf3156",
"target_record_id": "64b824a0-3527-4024-a1a2-5234f5b4caaa",
"expected_target_version": 1,
"content_sha256": "3ad808813909f1475f44a5fcf2a4e50c5d01e25ad8ea39d906c52519532cf0ee",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A03",
"title": "Programa de Desenvolvimento Integrado do Corredor de Nacala",
"source_record_id": "39f5973c-f4cd-49d0-aeea-1c0c71783550",
"target_record_id": "6c03e980-c455-49cc-a3be-99d29014e91d",
"expected_target_version": 1,
"content_sha256": "75266ff89da58e4d6e5422e49e4641c62dae4f171fa358b70e210c5883a1fd07",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A04",
"title": "Crusse Jamali – Zona Económica Especial de Turismo",
"source_record_id": "0dccdd26-9796-4ef7-947b-5a2970ace379",
"target_record_id": "8bd83cc4-b36c-46cd-9287-b47e1162b578",
"expected_target_version": 1,
"content_sha256": "f6a63e2ee2f2e7c98f1030d206996ad57b0d92129d021ed3c6bb53f161d56f5b",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A05",
"title": "Linha de Transmissão Namialo–Metoro (400 kV)",
"source_record_id": "361789ad-1e14-4b64-8f1f-fad053886d5a",
"target_record_id": "d8a88bac-4684-43fa-aea6-e54fef9b8a47",
"expected_target_version": 1,
"content_sha256": "ea0f6b7843289d81dc2852327432721f8677e28ad8b41d754abc8c91307303d3",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A06",
"title": "Cadeia de Valor do Arroz por Projecto de Processamento",
"source_record_id": "15474e6a-90a3-4db4-b456-eb1c1c66227c",
"target_record_id": "57a4ee7a-4c07-4e26-b8f5-e07726e41512",
"expected_target_version": 1,
"content_sha256": "c7fae7b2ca541e734e587589dace8dc85dee2494876de6e30ecd91a7177a985d",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A07",
"title": "Parque Têxtil Integrado",
"source_record_id": "22323abb-91d9-43ed-98f3-1194a5250c9e",
"target_record_id": "70ae731e-fdef-473f-b495-14f319b28d60",
"expected_target_version": 1,
"content_sha256": "3eb01ce3dd2cc68b43b816708f7e5f990c48f1aea6555cdfd169a286e11b9bd4",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A08",
"title": "Zona Económica Especial de Mocuba",
"source_record_id": "81d775e5-5dff-4fa2-98b1-72affb0a1082",
"target_record_id": "b30ecd8c-220d-4c7a-b1a7-704dc24d0933",
"expected_target_version": 1,
"content_sha256": "bca089a4bb2601ca34c50e2c48068b4c2f9fa7a873955cf0587f261a83c2a146",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A09",
"title": "Construção da Cidadela Parlamentar de Mocuba",
"source_record_id": "67263220-6849-468f-82f4-d5e1f5617190",
"target_record_id": "dc8892ff-fdda-480c-81f1-2ffe0bfcb40b",
"expected_target_version": 1,
"content_sha256": "7e3bb071f65e49245fbfd070f4123ac49985004e865c475bf7e34c85cc708aea",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A10",
"title": "Mphanda Nkuwa HPP – Hidroeléctrica",
"source_record_id": "cad01a02-6072-4a11-823a-1ae379bc65da",
"target_record_id": "77b685f0-848f-4800-83f8-a29539ed8f31",
"expected_target_version": 1,
"content_sha256": "02dc0fb152908e81b11304f6bc0c2252805ebd2ad1860b1a6bfa918e2cbcd5bb",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A11",
"title": "Corredor Ferroviário e Portuário de Macuse–Moatize",
"source_record_id": "72c13e83-e8dd-465d-99a9-eb0ccaa4fd0b",
"target_record_id": "cb125f84-6f75-48e5-a0de-2f5e6bd537ed",
"expected_target_version": 1,
"content_sha256": "736fc41d941740f41697c579ffe6c4208d45005d7177b7c42f006554a5dde480",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A12",
"title": "Programa de Conectividade e Resiliência Costeira (Compacto II)",
"source_record_id": "a023d6b2-1fa2-4fe7-9709-f93ae1f10e99",
"target_record_id": "71b96599-09c2-4809-b98f-acbf0c2b6f38",
"expected_target_version": 1,
"content_sha256": "9dbdb468b8644e07fabdc943ecde2f11ea561d5174c00559228ed29b2a29b1fe",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A13",
"title": "Vilanculos – Região Integrada de Turismo",
"source_record_id": "bb15487f-1617-49e6-86b7-d617812b658b",
"target_record_id": "5ce2d415-1c84-4625-a4cd-0f6738454318",
"expected_target_version": 1,
"content_sha256": "7a62afdda8e2c3fcc47b1c9032359906ff75dff9b5d56c71ba7d88781a8f8620",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A14",
"title": "Zona Económica Especial de Agronegócio do Limpopo",
"source_record_id": "6d973bb4-ab8a-49bc-92ed-286ed5b96fc4",
"target_record_id": "88111fb4-ab20-4393-bd1b-3a1694fb4ac4",
"expected_target_version": 1,
"content_sha256": "930836392126133499fa69313a4718ce2ec1f6c8f0839c184e9b5cc229460d1c",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "A15",
"title": "Regadio na Bacia Hidrográfica do Rio Maputo",
"source_record_id": "1468b8d2-1b08-4c84-8000-ece7662edbf6",
"target_record_id": "88e37c63-e785-446f-9074-84b000bffa59",
"expected_target_version": 1,
"content_sha256": "d19d27451296b9816351e2d340f2aaff9fabf9732fb580d179e1c69ceb9fc512",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "CLU-01",
"title": "Agro-indústria e segurança alimentar",
"source_record_id": "ca6a1427-0e2b-406e-94bd-da23630d5ec4",
"target_record_id": "0c63d8de-18fd-451f-a7ca-1ea04dd5906c",
"expected_target_version": 1,
"content_sha256": "ccd16f969474dcc23b2ddf27eb4cf3ca090f22e418e0e8f33499f349e884d042",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "CLU-02",
"title": "Energia e industrialização",
"source_record_id": "3cd11616-9099-477c-bf3e-09cddca74053",
"target_record_id": "fc1355f5-1473-4fe2-b3e2-b32d1f0b1042",
"expected_target_version": 1,
"content_sha256": "78c50659068301be0db94e8f4c9e77a20a4a052639dc4553291ff3568d3c0c4a",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "CLU-03",
"title": "Logística, corredores e exportação",
"source_record_id": "ec9494a2-22cc-423e-8a9e-efa1e05ac2b4",
"target_record_id": "99bf7c4c-e23b-47d5-8490-53082384043f",
"expected_target_version": 1,
"content_sha256": "99af7bb35b6769d84bee2175427b3a03027fea65a91dd894338fd7aa72db3820",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "CLU-04",
"title": "Turismo costeiro e economia azul",
"source_record_id": "5f0ff662-dc74-41de-adcd-de784a595321",
"target_record_id": "816b7c26-827b-475a-bf98-647b46afe88b",
"expected_target_version": 1,
"content_sha256": "a75f532e2464dadd13dd2ff815d99acfb2f05d3a0264fcf82dfe8e3db7db3a1f",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "CLU-05",
"title": "Têxtil, vestuário e algodão",
"source_record_id": "b9b1dc01-dca1-42d8-a9aa-40148dccfaf7",
"target_record_id": "8eb7da82-1f1e-442b-9b1e-57b948c3f057",
"expected_target_version": 1,
"content_sha256": "fe2f37be8e3f20ccc35da91aa86872b960e9820f2e0de84200c157072b3c7b11",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "CLU-06",
"title": "Mineração e transformação mineral",
"source_record_id": "0f29da56-b74a-4a69-afaa-b9a0d349580c",
"target_record_id": "65d709f5-20ed-4beb-8cf1-4a7fbd459e8f",
"expected_target_version": 1,
"content_sha256": "097498df28f63b614914cc171e4d58c6154442d0d6625185b3759878829528de",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "CLU-07",
"title": "Tecnologia e serviços digitais",
"source_record_id": "523d2aa1-4c7d-4a84-97fc-220f4e845fd7",
"target_record_id": "a795cde3-3cfe-456b-ba8d-7ff3730024ec",
"expected_target_version": 1,
"content_sha256": "6c5407281d18069e05b84e1e7997d07fd79680e2b0e2a5b0e7d86cbfc3c2ca35",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "CLU-08",
"title": "Financiamento do investimento",
"source_record_id": "ed2ea4e4-fca7-4cc8-bb8c-f99a1dfda73e",
"target_record_id": "05dc87a8-e1ab-41c9-a312-5b14b8ff16ce",
"expected_target_version": 1,
"content_sha256": "f59505944a91a849c27c16455001ad4a1d39aa58e59e8d5403cb0dc197c2faf8",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "COR-01",
"title": "Corredor de Nacala e Norte",
"source_record_id": "dc8f048a-c0bd-476e-9ff2-460dfb12eddc",
"target_record_id": "986af8c5-2f90-40ab-a62a-4c37a5f42e30",
"expected_target_version": 1,
"content_sha256": "26db6621cae0e76d2871f56926062bc0be5bb41e21521d2536655e809b6dff20",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "COR-02",
"title": "Corredor da Beira e Centro",
"source_record_id": "fd5e7b2d-cb48-49e4-bb96-1520263144f2",
"target_record_id": "a7271336-808b-42b4-8bfd-5b49c72dbe2b",
"expected_target_version": 1,
"content_sha256": "0e6b80849fcb0746aa1a53133ee0d5ccc3df8c847243a1134922247ce3d9e1b8",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "COR-03",
"title": "Corredor Zambeze–Macuse",
"source_record_id": "18f3a86c-af85-4fbb-b045-9c3e03c4a610",
"target_record_id": "d9ccddb5-e6aa-4347-a481-37b694013ac1",
"expected_target_version": 1,
"content_sha256": "64c8eba636661e6ebb56e2dd8dd6e978c405dd96c0c7b46bdd62c805018a6c4a",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "COR-04",
"title": "Corredor Maputo–Limpopo",
"source_record_id": "36d72f82-ff0a-468c-89bf-5dc9ae12eddf",
"target_record_id": "ae26d529-5341-4b37-ad08-662c9d09a0cf",
"expected_target_version": 1,
"content_sha256": "ce5b89974e1d2937e4901ab5603245cf2465afbe0a44a224c43f726069cdabd8",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "COR-05",
"title": "Corredor Turístico Inhambane–Bazaruto",
"source_record_id": "06b2d5d7-98ad-48db-b732-8a70b2f5eb4c",
"target_record_id": "69767dcc-4c22-4046-8f81-3797bc44572e",
"expected_target_version": 1,
"content_sha256": "343ba56efeef5514f531cf61126596a8c65f882656da5f95390eb661a0810a7c",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
},
{
"code": "COR-06",
"title": "Corredor Industrial Nacional Norte–Sul",
"source_record_id": "6727e46b-98e1-47a7-be04-dfdb1c6375cb",
"target_record_id": "fb85ebfc-798c-42df-a267-d4b771c443f8",
"expected_target_version": 1,
"content_sha256": "465ff9717def3bad42efb25d1ffa04812f871e8b167adcbb20f68f5ef9460b57",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"approval_evidence": "26/09/2026 — Aprovação expressa do proprietário nesta sessão: inserir a informação do documento upgrade do portal de investimentos v4 programacao sobre clusters, corredores e projectos ancora nas tabelas e criar os caminhos para a sua execução; eu e que aprovo e estou a aprovar.",
"approval_sha256": "0cbc4b7a3f01dfb6c28659a11597f7e10f87f8c3119bf44ea92b97bec033f3eb",
"original_published_at": "2026-09-26 16:22:52+00"
}
]
}
@@ -0,0 +1,31 @@
{
"target": "https://teste-investidor.tempo.co.mz",
"authorization": "Tempo, 23/10/2026: concluir visual QA, integrar pelo Git e conferir apenas os tres deltas instalados",
"status": "installed",
"commit": "6b544548d3c83fc8a72ab65d8f58f84685a2877f",
"finished_at": "2026-10-23T13:45:19.950347+00:00",
"manifest_sha256": "65a694b5d81f833446a308f48f4966b32a8bdc5421f1ee2196b79ac0b798b636",
"release": "/var/www/investmoz-homologacao/releases/6b544548d3c83fc8a72ab65d8f58f84685a2877f-b38d94da",
"previous_release": "/var/www/investmoz-homologacao/releases/7c1f532134e4492dec010d0c511f669a1e2c23a7-524bd081",
"git": "Fast-forward from 7c1f5321; no remote-exclusive main changes; no force push",
"database_migrations": 0,
"content_imports": 0,
"permission_changes": 0,
"automatic_frontend_tests": {"tests": 206, "passed": 206, "failed": 0},
"qa_visual_completed": "Beira page displays seven documented opportunities; Florestas opens and both preparation links retain from_dossier",
"destination_delta": {
"language": "Existing private TESTE DE HOMOLOGACAO dossier: Portuguese (Mozambique) selected, explicit save confirmed, page reload recovered the same language without false conflict",
"test_dossier": "958df138-7980-4685-8389-fa90be02ed6b",
"simulation_draft": "6c07a968-7ff9-42fa-b252-072bb1acf221",
"report": "Printed report revision 4 distinguishes title from user dossier data, summary inherited from Florestas opportunity version 2, sector from sector catalogue and Sofala from territorial catalogue",
"beira": "Seven detail pages opened from observed links; exact titles and from_dossier preserved in preparation links",
"beira_titles": ["Açúcar — Corredor da Beira", "Carne bovina — Corredor da Beira", "Florestas — Corredor da Beira", "Hortícolas — Corredor da Beira", "Milho, trigo e arroz — Corredor da Beira", "Sésamo — Corredor da Beira", "Soja — Corredor da Beira"],
"stale_versions": "Accepted QA rejection tests reused; installed server guard bytes match the tested version. No deliberate stale write against destination user data",
"version_guard_sha256": "1626280b34481298de964ff82027ae7ea9b1aa80d1b86554987edbe6ec5a087f",
"draft_client_sha256": "dfe1a44e8a9af48735d75d0742583ce2280abf7615049d569f7963763bdd5e4c"
},
"local_proofs": ["outputs/preservation-inventory-20261023/beira-seven-visual-QA.png", "outputs/preservation-inventory-20261023/installed-language-recovery-6b544548.png", "outputs/preservation-inventory-20261023/installed-provenance-report-6b544548.png", "outputs/preservation-inventory-20261023/installed-beira-seven-6b544548.png"],
"preservation": "No republication of the 34 opportunities or reimport of portfolio; only existing labelled private test dossier used for writes; previous release preserved",
"rollback": "Existing release rollback; retain database, uploads, permissions, history and later edits",
"remaining": "Transversal application remains partial; this receipt closes only the three installed correction deltas"
}
@@ -0,0 +1,25 @@
{
"decision_owner": "Tempo, Lda.",
"decision_date": "2026-10-23",
"configuration": "config/investment_corridors.php",
"functional_revision": "0025309b",
"maputo_final_decision": ["Maputo Província", "Maputo Cidade"],
"automatic_province": {"nacala": "Nampula", "limpopo": "Gaza"},
"existing_territorial_ids_reused": true,
"server_validation": ["corridor and territory", "documented zone compatibility", "actor and dossier access", "draft request ordering", "dossier concurrency"],
"recovery": "Encrypted pending draft in the existing dossier payload, per actor; distinct from confirmed context.",
"qa": {
"database": "simulator_fiscal_delta_20261021_test",
"port": 55441,
"focused_tests": 5,
"assertions": 90,
"existing_opportunity_continuity_tests": "passed",
"types": "passed",
"build": "passed",
"browser": ["Beira offers Sofala and Manica", "Nacala selects Nampula", "Maputo offers both units", "Sofala/Dondo recovers after reload", "normal context confirmation succeeds"],
"browser_account_and_dossier": "synthetic and private, QA only",
"screenshot": "outputs/preservation-inventory-20261023/corridor-adaptive-territory-QA.png"
},
"homologation": {"git_integrated": true, "public_read_check": "Maputo province endpoint returns exactly the two existing units", "real_dossier_test_writes": false},
"installation": {"migrations": 0, "imports": 0, "republication": false, "new_roles": false, "geometry_changes": false, "automatic_fiscal_calculation": false}
}
@@ -0,0 +1,253 @@
{
"date": "2026-10-23",
"timezone": "Africa/Johannesburg",
"status": "installed_in_homologation_delta_verified",
"baseline_commit": "4098192a056959d788e6f9ff5cd501211b001f39",
"preserved_acceptance": {
"delivery": "6b544548d3c83fc8a72ab65d8f58f84685a2877f",
"receipt": "4098192a056959d788e6f9ff5cd501211b001f39",
"cases_repeated": false
},
"cause": {
"portfolio_field": "content.focus",
"previous_contract": "string rendered as descriptive text; no complementary selection or typed origin-target relationship in the dossier",
"data_loss_claimed": false
},
"scope": {
"components": [
"irrigation",
"storage",
"cold",
"processing",
"spinning",
"logistics",
"certification",
"training",
"finance"
],
"existing_opportunity_identity_preserved": true,
"documented_sources": [
"portfolio associations including the existing accepted documentary membership",
"public complementary_project_links",
"existing publicly disclosed value-chain references",
"published offering partnerships explicitly associated with the origin"
],
"distinct_states": [
"registered opportunity",
"identified complementary need without a matched offer",
"existing service or instrument",
"previously selected relationship requiring review"
],
"dossier": [
"encrypted existing payload",
"draft",
"preview and confirmation",
"explicit removal and reselection",
"existing audit history",
"source and territory preserved",
"pending edits saved before complementary navigation"
],
"consumers": [
"dossier",
"shared prefill component for simulator, land, desk and submission",
"simulator context snapshot",
"shared report and export rows"
]
},
"validation": {
"database": "simulator_fiscal_delta_20261021_test",
"host": "127.0.0.1",
"port": 55441,
"database_identity_guard": true,
"test_transaction_rollback": true,
"feature_test_file": "tests/Feature/InvestmentComplementsTest.php",
"feature_tests_passed": 8,
"feature_assertions": 134,
"frontend_test_file": "tests/frontend/investment-complements.test.mjs",
"frontend_tests_passed": 2,
"typescript": "passed",
"build": "passed",
"php_syntax": "passed",
"diff_whitespace_check": "passed",
"public_chain_branch": "synthetic public projection contract; not evidence of new real-world chain associations",
"browser_visual_verification": "passed_for_the_requested_complement_scope"
},
"visual_verification": {
"date": "2026-10-23",
"environment": "http://127.0.0.1:18574",
"router": "tools/corridor-agribusiness-qa-router.php",
"database_guard_checked_on_requests": true,
"actor": "existing synthetic QA actor 509; existing normal browser session",
"case_id": "6fdbb963-726e-4048-9884-8cddc04e77a6",
"case_title": "SYNTHETIC QA — conferência visual de complementos 23-10-2026",
"existing_opportunity": {
"id": 286,
"reference": "INV-MZ-GEN-8128691124566423",
"slug": "inv-mz-gen-8128691124566423",
"result": "Opened the existing fiche from the storage complement; the preparation entry retained the portfolio origin and the existing identity. No reimport or duplicate."
},
"need_without_identified_offer": {
"code": "spinning",
"origin": "portfolio:a7271336-808b-42b4-8bfd-5b49c72dbe2b",
"result": "Started idea preparation in the same dossier; recovered as a complementary need with location still undetermined, without inventing an offer or inheriting Sofala."
},
"associated_service": {
"id": 17,
"synthetic_fixture": true,
"origin": "opportunity:309",
"territory": "Gaza",
"result": "Opened the existing QA service fiche, selected it through the return action, confirmed it and recovered it in the same dossier."
},
"multiple_opportunities": {
"first": {
"id": 286,
"territory": "Sofala"
},
"second": {
"id": 309,
"territory": "Gaza",
"synthetic_fixture": true
},
"complementary_opportunity": {
"id": 310,
"origin_id": 309,
"territory": "Gaza",
"synthetic_fixture": true
},
"service_origin_id": 309,
"recovered_after_confirmation_and_browser_reload": true,
"complements_assigned_to_first_automatically": false
},
"fixture_preparation": {
"previous_service_offerings_in_qa": 0,
"new_synthetic_records": "Two projects, two opportunities, one explicit complementary-project link and one associated training offering, solely in the guarded QA database; no grants or real publication/approval claims.",
"preexisting_records_preserved_by_hash": {
"opportunities": 45,
"projects": 17,
"investment_dossiers": 30,
"portal_partnerships": 0
},
"fixtures_and_visual_case_persisted_in_qa": true,
"fixture_receipt": "outputs/complements-visual-20261023/fixture.json"
},
"evidence_directory": "outputs/complements-visual-20261023",
"automated_suites_repeated": false,
"functional_code_changes_in_this_visual_pass": false,
"does_not_demonstrate": [
"multi-opportunity zones and parks",
"specific per-opportunity context in land assistance",
"specific per-opportunity context in virtual desk",
"specific per-opportunity context in manifestation",
"availability of a real training or financing service in homologation"
],
"evidence_files": [
{
"path": "outputs/complements-visual-20261023/existing-opportunity.png",
"bytes": 112261,
"sha256": "93425703b17c0976a5728acc7448faa0b7dd73e34973cb68ebf72cb9d416dd58"
},
{
"path": "outputs/complements-visual-20261023/associated-service.png",
"bytes": 117223,
"sha256": "a5d16746b4264b779430cc3843959492bea41a3da2adc00298fa2133c99aef9d"
},
{
"path": "outputs/complements-visual-20261023/recovered-opportunities.png",
"bytes": 99910,
"sha256": "eb65dd0d42af0c9f553db6756760dcc16f598b2da7b3d0157b6faea3dbebf24e"
},
{
"path": "outputs/complements-visual-20261023/recovered-complements.png",
"bytes": 147972,
"sha256": "4e2db825710665298803542fe9c7f299404962503609948ddd083c3daf195a73"
},
{
"path": "outputs/complements-visual-20261023/recovered-visible-state.json",
"bytes": 2304,
"sha256": "d2ada1b501db973f1492a54f1704e9cb2de112e38cacd4ab916f059dbbd11f18"
},
{
"path": "outputs/complements-visual-20261023/fixture.json",
"bytes": 904,
"sha256": "79fa2ac8196ca5620b258c78332a2462b44f2c5b622fc0c1895fb39199efcd67"
}
]
},
"preservation": {
"active_database_writes": false,
"homologation_installation": false,
"migration": false,
"publication": false,
"import": false,
"new_roles_or_grants": false,
"new_matrix_or_backlog": false
},
"matrix_rows": [
"RF-CLU-003",
"RF-COR-004",
"AUD-REL-02",
"AUD-LEG-06",
"AUD-SIM-INT06"
],
"limitations": [
"Visual verification is limited to the documented QA scenarios; no Tempo acceptance or destination installation is claimed.",
"The resolver covers the nine requested component scopes in available documentary contexts; unmatched expressions and absent relationships are not fabricated.",
"No matched offer means none identified in the examined relationships, not proof that the entire collection has no offer.",
"The existing first-opportunity zone/park selection and complete per-opportunity requirements in other forms remain partial.",
"No global completion of the adaptive journey is claimed."
],
"installation": {
"status": "installed_delta_verified",
"date": "2026-10-23",
"target": "https://teste-investidor.tempo.co.mz",
"served_commit": "d8224ba84b56482e0291bc18bb1c9c29316b19b9",
"installer": {
"status": "installed",
"commit": "d8224ba84b56482e0291bc18bb1c9c29316b19b9",
"release": "/var/www/investmoz-homologacao/releases/d8224ba84b56482e0291bc18bb1c9c29316b19b9-c0d7c675",
"previous_release": "/var/www/investmoz-homologacao/releases/6b544548d3c83fc8a72ab65d8f58f84685a2877f-b38d94da",
"database_migrations": 0,
"content_imports": 0,
"requested_commits": [
"d8224ba84b56482e0291bc18bb1c9c29316b19b9"
],
"finished_at": "2026-10-23T15:12:40.124788+00:00"
},
"public_revision": {
"commit": "d8224ba84b56482e0291bc18bb1c9c29316b19b9",
"branch": "main",
"built_at": "2026-10-23T15:12:37.169518+00:00",
"manifest_sha256": "52a021c1281e72d2aabe4a8fad82a150857e851d917c28e6898d2d439dfb771f"
},
"verification": {
"qa_guard_assertions": 3106,
"unit_tests": 57,
"unit_assertions": 309,
"frontend_tests": 208,
"frontend_failures": 0,
"public_real_fiche_opened": "INV-MZ-GEN-6048328347519124",
"real_target_id": 48,
"origin_preserved": "portfolio:a7271336-808b-42b4-8bfd-5b49c72dbe2b",
"spinning": "need_without_identified_offer_location_undefined",
"service": "existing_publication_states_no_synthetic_fixture_transferred",
"manual_business_cases_repeated": false
},
"preservation": {
"concurrent_main": "43cd0be7cbe2450f1928b059f1456454f23943ef",
"integration": "088f2c2cf47f5b16f351e231c490e848a664e433",
"accepted_delivery": "6b544548",
"accepted_receipt": "4098192a",
"database_migrations": 0,
"content_imports": 0,
"qa_fixture_payload_shipped": false,
"transversal_coverage": "partial"
},
"screenshot": {
"path": "outputs/complements-install-20261023/installed-complements.png",
"sha256": "9d48d2a51d5fbb1c0f5e92247af399c582444b329406416c172848ee7b1cb1e4"
},
"pending": [
"multi-opportunity specific contexts in zones/parks, Terra/DUAT, Balcao and manifestation remain to be demonstrated"
]
}
}
@@ -0,0 +1,89 @@
{
"date": "2026-10-23",
"status": "installed_in_homologation_delta_verified",
"functional_commit": "1afdfee8",
"base": "bc84fbdb",
"accepted_deliveries_preserved": [
"6b544548",
"d8224ba8",
"55136b44"
],
"qa": {
"database": "simulator_fiscal_delta_20261021_test",
"port": 55441,
"guard": "asserted in tests and fixture preparation",
"case_id": "d279172b-8bd1-4074-b000-ee25f0d29778",
"actor": "synthetic existing QA actor 509",
"browser": "Edge http://127.0.0.1:18574",
"existing_accepted_cases_modified": false
},
"changes": [
"simulator explicit or inherited opportunity key saved and recovered",
"applicability facts reasons and stage details by opportunity without replacing common data",
"land shortcut filters dossier owner and chosen opportunity",
"requirement and return links preserve scope",
"authorised common contact proposals where duplication was found",
"Word/ZIP/print composition identifies selected scope and applicable stages"
],
"verification": {
"http_tests": 7,
"http_assertions": 170,
"http_suite": "tests/Feature/InvestmentTreatmentScopeTest.php",
"frontend_tests": 2,
"frontend_suite": "tests/frontend/treatment-scope-export.test.mjs",
"typescript": "passed",
"production_build": "passed",
"visual": "new QA dossier second Manica / first Sofala / second; general simulator selection and scoped return to dossier"
},
"remaining": [
{
"scope": "native PDF",
"status": "only saved native PDF pagination and legibility remain unverified; HTML is not that proof",
"limitation": "browser-only control does not expose system print/save dialogue; no failure attributed to application"
},
{
"scope": "anchor projects sector and location search with manifestation and investment preparation continuity",
"status": "to analyse; no inference of implementation or defect",
"matrix": "AUD-REL-02"
}
],
"limits": [
"no homologation deployment or active data/schema/config/permission change",
"no repeat of accepted complement or four-consumer tests",
"no new visual proof of every alternative entry or removal editor; directed HTTP proof only",
"no statistics or AI work",
"temporary fixture clone location confirmation and test version field corrected before passing results"
],
"artifacts": [
{
"path": "outputs/multi-opportunity-fix-20261023/treatment-dossier.png",
"sha256": "2b5f65064c80e376efdb2d12c87725c042027296f1e4ab6536f140462482562f"
},
{
"path": "outputs/multi-opportunity-fix-20261023/treatment-dossier.txt",
"sha256": "0887a3ab5da2c6eccd369c7ca691df9d5f8d92279f6202eddf15b1652c138c89"
},
{
"path": "outputs/multi-opportunity-fix-20261023/treatment-simulator.txt",
"sha256": "410f91d3afe220ec46f435c9de62d34671b2096d961527d0ce97a3edb9838b34"
},
{
"path": "outputs/multi-opportunity-fix-20261023/treatment-exports/second.docx",
"sha256": "7aeadecb3684d000d5fe0c3b93d339f380c434daf3890ae24bd79c2f723ae40b"
},
{
"path": "outputs/multi-opportunity-fix-20261023/treatment-exports/second.zip",
"sha256": "a3c1100d9c9ffc75fbda0f0489479c17275e8fa0b3025ad3ae2ecb293a9aa1f8"
},
{
"path": "outputs/multi-opportunity-fix-20261023/treatment-exports/second-print.html",
"sha256": "dffff89a9d4f186698bbc077a8a5031bd95290e12bcdba7b00ec7cd1a095bc4a"
}
],
"installation": {
"status": "installed_in_homologation_delta_verified",
"commit": "8036b9b071229dbf7e85ea748db70abcbeefc379",
"receipt": "treatment-scope-installation-receipt-20261023.json",
"delta_verified": true
}
}
@@ -0,0 +1,657 @@
{
"scope": "homologation private registration, not publication or editorial acceptance",
"functional_revision": "65eddf6bc7025c81cbd7b0c5918b6ae8bdb1c1f6",
"candidate": "fb4e7d897083957486385aa517969afd1a10b417",
"checked_utc": "2026-10-22T21:54:25+00:00",
"checked_Africa_Johannesburg": "2026-10-22T23:54:25+02:00",
"management_url": "https://teste-investidor.tempo.co.mz/gestao-conteudos",
"catalogue_url": "https://teste-investidor.tempo.co.mz/management/portal-services?state=draft",
"actor_id": 15,
"account": "Osmane Nala",
"real_password_MFA_session": true,
"MFA_fabricated_or_extended": false,
"mechanism": "Existing authenticated HTTPS upload, metadata and editorial endpoints using the transported server originals; no direct SQL data writes",
"archive_retransported": false,
"archive_sha256": "177c50d1cc3b1610e62123a2f8b532711a15f875aac2ee0e372636c2f747e8a2",
"backup_restore": {
"restored_tables": 174,
"files_verified": 341,
"dump_sha256": "8658b5fa0d1ef16c9fc9be90969badde6007443aa474fe4a9377f16d80fe6e92",
"files_archive_sha256": "1d98b6200ffae11bb07dc600daa6416b3b1b90735de42791858a07a0caa63552"
},
"preservation": {
"target": {
"name": "teste_investidor",
"port": 5432
},
"existing_rows_preserved": {
"documents": 32,
"portal_resources": 31,
"portal_resource_revisions": 63,
"portal_resource_links": 19,
"users": 17,
"geo_locations": 1921,
"geo_geometries": 408
},
"grants_preserved": {
"portal_role_assignments": true,
"portal_superuser_pair": true,
"portal_security_approvals": true
},
"new_private_articles": 28,
"new_publications": 0,
"new_originals": 28,
"clean_scans_matching_bytes": 28,
"original_sha256_unchanged": true,
"checked_utc": "2026-10-22T21:54:25+00:00"
},
"rows": [
{
"external_id": "legacy-tempo-post-62413-20170626",
"legacy_id": 62413,
"title": "15/30 costa do sol com dua na zona de mupulene muito lindo",
"source_url": "https://investemoz.tempo.co.mz/1530-costa-do-sol-com-dua-na-zona-de-mupulene-muito-lindo",
"resource_id": 32,
"document_id": "c20a1729-0a93-4b9e-beab-13b1320f6587",
"sha256": "b471c1afd7235f359acdcc16a93088f77e1f988706c8d3c4d4f11c5d560aea47",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 3,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-73945-20170731",
"legacy_id": 73945,
"title": "Banco Mundial classifica de proibitivos juros praticados em Moçambique",
"source_url": "https://investemoz.tempo.co.mz/banco-mundial-classifica-de-proibitivos-juros-praticados-em-mocambique",
"resource_id": 33,
"document_id": "5c550542-79c4-4b86-bb12-3db3e8daa631",
"sha256": "2aa0ce0eb305bfdf93e390a978d3694d0fbc0129034077b68cebce418f878415",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-76474-20170808",
"legacy_id": 76474,
"title": "Coloque o seu número para o grupo de whatsapp, venda de acessórios de computador",
"source_url": "https://investemoz.tempo.co.mz/coloque-o-seu-numero-para-o-grupo-de-whatsapp-venda-de-acessorios-de-computador",
"resource_id": 34,
"document_id": "baff8e03-3327-40a0-af01-3dd11d163871",
"sha256": "8f769aec4715449d056c8c25294060b2ff290ec0893c28e9bdcc4e8092fc75f5",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 2,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-62414-20170626",
"legacy_id": 62414,
"title": "Frelimo e Zanu-PF: Evocados laços de irmandade",
"source_url": "https://investemoz.tempo.co.mz/frelimo-e-zanu-pf-evocados-lacos-de-irmandade",
"resource_id": 35,
"document_id": "a5050bd9-e868-4347-9a24-03c2b6c04ab9",
"sha256": "a040c0b7cb95dd7f053f4d13662dd58b2b06453b6fa61a2d531d0257a8db8c17",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-62418-20170626",
"legacy_id": 62418,
"title": "Governo dará todo O apoio à PGR no seguimento da auditoria",
"source_url": "https://investemoz.tempo.co.mz/governo-dara-todo-o-apoio-a-pgr-no-seguimento-da-auditoria",
"resource_id": 36,
"document_id": "c3d4031c-62c8-4bbf-ab6c-193412036888",
"sha256": "e8ccafa68f0d1aa80f2e41d9e09f7b13e7c653a8472a731798cfd5e04ce2963e",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-75002-20170804",
"legacy_id": 75002,
"title": "Governo pretende construir 100 mil habitações em dois anos",
"source_url": "https://investemoz.tempo.co.mz/governo-pretende-construir-100-mil-habitacoes-em-dois-anos",
"resource_id": 37,
"document_id": "97eeceb3-f61e-4980-a884-3228f4707af8",
"sha256": "7b6fbd998be2b47b96e9a4724560c192c502709fa308d6590c0797ddcdb10544",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-73246-20170728",
"legacy_id": 73246,
"title": "Rofino Licuco preso cinco meses depois de condenado por agressão",
"source_url": "https://investemoz.tempo.co.mz/rofino-licuco-preso-cinco-meses-depois-de-condenado-por-agressao",
"resource_id": 38,
"document_id": "409b9017-d89e-42b3-a7ec-741f2f1f7683",
"sha256": "430ee8674c71104fc126bed2911d676f150ac623857a1ada75662987fd9ac390",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-77232-20170811",
"legacy_id": 77232,
"title": "Sete indivíduos mortos a tiro na Matola",
"source_url": "https://investemoz.tempo.co.mz/sete-individuos-mortos-a-tiro-na-matola",
"resource_id": 39,
"document_id": "5d0a813f-9001-4d04-9b84-865977c2f1f2",
"sha256": "27544d3127803b7495bef9fd633147d0225d8c31816c4d59ed05a2edd59de070",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-62416-20170626",
"legacy_id": 62416,
"title": "Sete pessoas morrem e 150 são resgatados em acidente com eléctrico na Índia",
"source_url": "https://investemoz.tempo.co.mz/sete-pessoas-morrem-e-150-sao-resgatados-em-acidente-com-electrico-na-india",
"resource_id": 40,
"document_id": "3856ae7f-be39-4926-b653-cfd67aeaf003",
"sha256": "ac575ce340571192552c89b63f0bd62a84dbc47828e6930133af5ff70e249319",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-62929-20170627",
"legacy_id": 62929,
"title": "Daúde Razaque já não é treinador da Liga Desportiva de Maputo",
"source_url": "https://investemoz.tempo.co.mz/daude-razaque-ja-nao-e-treinador-da-liga-desportiva-de-maputo",
"resource_id": 41,
"document_id": "8f8b749d-1a2b-4e87-93b9-f89a70bdb6a8",
"sha256": "488017b062c6760659d9147b7f12aea4afd8d824121adb9462e2c7a2ae58437b",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-62649-20170626",
"legacy_id": 62649,
"title": "Daviz Simango não se incomoda com possível sucessão na Beira",
"source_url": "https://investemoz.tempo.co.mz/daviz-simango-nao-se-incomoda-com-possivel-sucessao-na-beira",
"resource_id": 42,
"document_id": "3060923e-c1c0-4b9c-bafb-15f61d53fcc9",
"sha256": "38a858713472759a2d20a308bdd9d8afa9cfb76dcaf8d11ca709e89261573e42",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-62739-20170626",
"legacy_id": 62739,
"title": "“Exigimos responsabilização dos agentes do crime”, Daviz Simango",
"source_url": "https://investemoz.tempo.co.mz/exigimos-responsabilizacao-dos-agentes-do-crime-daviz-simango-2",
"resource_id": 43,
"document_id": "674b3cbf-d7ff-4521-8650-b420837dbdd5",
"sha256": "5870fc0fad9bb7af537b7b1468151cde138b1b886ce846c7be25647b630245dd",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-62854-20170627",
"legacy_id": 62854,
"title": "Governo e parceiros reforçam mecanismos de controlo de HIV/Sida",
"source_url": "https://investemoz.tempo.co.mz/governo-e-parceiros-reforcam-mecanismos-de-controlo-de-hivsida",
"resource_id": 44,
"document_id": "a2469350-35d7-4d70-8574-a46975360016",
"sha256": "44b1ba9d593bb625cf53de8909e78eb0b3129742c3ec9d58ec68c984e8930bce",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-63128-20170627",
"legacy_id": 63128,
"title": "Graça Machel diz que desnutrição é um problema de desenvolvimento",
"source_url": "https://investemoz.tempo.co.mz/graca-machel-diz-que-desnutricao-e-um-problema-de-desenvolvimento",
"resource_id": 45,
"document_id": "f07797a0-4dcc-4706-acfd-2424b6bd6130",
"sha256": "74be8f29e5053f53ab1a39a2f63208145c30f23357ca6d78b637dd02bf9c1fb4",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-63181-20170627",
"legacy_id": 63181,
"title": "In dependência",
"source_url": "https://investemoz.tempo.co.mz/in-dependencia",
"resource_id": 46,
"document_id": "34d11ba7-086e-4290-9181-0c6adebc6658",
"sha256": "3d0f9ac14d36156d8178e33f8616d6406f31ea4cd69c8308cbf4227586d7ccb3",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-63146-20170627",
"legacy_id": 63146,
"title": "Kroll não descobriu como foram gastos 2 biliões de dólares; Governo de Nyusi não apoiou a Auditoria; criação da Proindicus, EMATUM e MAM não foi para proteção da Zona Económica Exclusiva",
"source_url": "https://investemoz.tempo.co.mz/kroll-nao-descobriu-como-foram-gastos-2-bilioes-de-dolares-governo-de-nyusi-nao-apoiou-a-auditoria-criacao-da-proindicus-ematum-e-mam-nao-foi-para-protecao-da-zona-economica-exclusiva-2",
"resource_id": 47,
"document_id": "27d98ca7-b59f-478f-8560-a51db4354069",
"sha256": "542353466cc6d82b651619c495f138b0576a4be41d21aad194ae2005050cb1bc",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-62688-20170626",
"legacy_id": 62688,
"title": "O Instituto Nacional de Meteorologia prevê o seguinte estado do tempo para esta ...",
"source_url": "https://investemoz.tempo.co.mz/o-instituto-nacional-de-meteorologia-preve-o-seguinte-estado-do-tempo-para-esta-29",
"resource_id": 48,
"document_id": "200a6292-c01b-4c83-bf3c-959379676651",
"sha256": "a210a789b06d425822f4124f74226a2b27246c086c5ec5f041f1b2237d7c2b65",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-63248-20170628",
"legacy_id": 63248,
"title": "Dois alunos da 9ª classe caíram nas mãos da Polícia da República de Moçambique (...",
"source_url": "https://investemoz.tempo.co.mz/dois-alunos-da-9a-classe-cairam-nas-maos-da-policia-da-republica-de-mocambique",
"resource_id": 49,
"document_id": "fc19fc89-4589-48b3-8328-9398ad367650",
"sha256": "6c30e61197b8568332af6d9b19bbba7ad8665b3873bb031a8c8aedd41536b060",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173220-20260616",
"legacy_id": 173220,
"title": "Alumínio “Verde”",
"source_url": "https://investemoz.tempo.co.mz/aluminio-verde",
"resource_id": 50,
"document_id": "9b3b5f7e-904e-4ac0-a389-b93fbc4f5ef6",
"sha256": "5b76c3d84dcf7f9a489686d90e3857ae6536705c918e841310bb42b354e80976",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173222-20260616",
"legacy_id": 173222,
"title": "Energia e Recursos Naturais",
"source_url": "https://investemoz.tempo.co.mz/energia-recursos-naturais",
"resource_id": 51,
"document_id": "7a4a544f-8159-47e7-b69c-b7a83f8324b1",
"sha256": "e64d7063d1ac4dda028c97e1ba64938d2a9e426a0b630c57ee5df52a41312cc7",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173482-20260915",
"legacy_id": 173482,
"title": "Checklist para Contratação e Direitos do Trabalho Doméstico",
"source_url": "https://investemoz.tempo.co.mz/checklist-para-contratacao-e-direitos-do-trabalho-domestico",
"resource_id": 52,
"document_id": "bbe4154b-c1d1-4ab3-8a36-92c4764df0a3",
"sha256": "6f888e133a779c719073246f89bcae150198e5897c0e7d9b7ba75f54c54a7830",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173484-20260915",
"legacy_id": 173484,
"title": "Direitos Especiais da Trabalhadora Doméstica no Regulamento",
"source_url": "https://investemoz.tempo.co.mz/direitos-especiais-da-trabalhadora-domestica-no-regulamento",
"resource_id": 53,
"document_id": "712ed069-72de-4cab-bc81-6d589aa22cb9",
"sha256": "297df455a9c6fd1f0ab7ca7bbe124ce483c86fe55be26baa695f03089b3ac049",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173478-20260915",
"legacy_id": 173478,
"title": "Guia Prático para Empregadores sobre Contratação e Direitos no Trabalho Doméstico",
"source_url": "https://investemoz.tempo.co.mz/guia-pratico-para-empregadores-sobre-contratacao-e-direitos-no-trabalho-domestico",
"resource_id": 54,
"document_id": "ba35f2e7-3d75-4dfc-b2e8-3e18425419ec",
"sha256": "d4e320ed99dd9d53d205e47932f73651976d43081d5d39f2bfc2996901408644",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173480-20260915",
"legacy_id": 173480,
"title": "Modelo de Contrato de Trabalho Doméstico para Pequenas Famílias",
"source_url": "https://investemoz.tempo.co.mz/modelo-de-contrato-de-trabalho-domestico-para-pequenas-familias",
"resource_id": 55,
"document_id": "e48a87d1-36fd-4e03-aece-a5184e390eff",
"sha256": "c3ee6401eda5b582c61969b5e3b74fdf26ef34ca481b1aca8c4c309d845147ae",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173476-20260915",
"legacy_id": 173476,
"title": "Procedimentos para Rescisão do Contrato de Trabalho Doméstico segundo o Regulamento de Moçambique",
"source_url": "https://investemoz.tempo.co.mz/procedimentos-para-rescisao-do-contrato-de-trabalho-domestico-segundo-o-regulamento-de-mocambique",
"resource_id": 56,
"document_id": "57dd9068-c5c1-406a-bbc1-d0fdb79f038e",
"sha256": "72fbf28391db4804e0637b64e7f59e9fa597812a2681b4562dc3d6dfb4093a30",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173470-20260915",
"legacy_id": 173470,
"title": "Direitos e Deveres do Trabalhador Doméstico",
"source_url": "https://investemoz.tempo.co.mz/direitos-e-deveres-do-trabalhador-domestico",
"resource_id": 57,
"document_id": "de254617-49c2-407d-9e3c-e466488dd46b",
"sha256": "26feeae6b8d04972c7a64ee360583cccdcaeb4b0d8795fd7bc2fd3c38866cc19",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173474-20260915",
"legacy_id": 173474,
"title": "Sanções Disciplinares Aplicáveis a Trabalhadores Domésticos segundo o Regulamento de Moçambique",
"source_url": "https://investemoz.tempo.co.mz/sancoes-disciplinares-aplicaveis-a-trabalhadores-domesticos-segundo-o-regulamento-de-mocambique",
"resource_id": 58,
"document_id": "17e4a9cb-286a-4c87-a5bc-9f1e91a5da44",
"sha256": "9fa741a17b746bfa2f3ce7b6ce1d2bb34d3a35d3b58330b0554f9e0a6844f049",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
},
{
"external_id": "legacy-tempo-post-173463-20260915",
"legacy_id": 173463,
"title": "Regulamento do Trabalho Doméstico",
"source_url": "https://investemoz.tempo.co.mz/regulamento-do-trabalho-domestico",
"resource_id": 59,
"document_id": "20ce1dd9-729f-458f-b650-f5eb55c56d60",
"sha256": "4f34b7bac4b3030c0614844d379f6e3cfad1b7f6ea3db575a58c33ae033b0e73",
"scan": "clean",
"scanned_sha256_matches": true,
"private": true,
"metadata_version": 1,
"search_http": 200,
"edit_http": 200,
"preview_http": 200,
"anonymous_article_http": 404,
"image_href": "/documentos/27/view",
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"edition_recovered": true
}
],
"UI_demonstration": {
"management_search": "15/30",
"resource_id": 32,
"save_and_recover_version": 3,
"body_changed_in_destination_UI": false,
"private_preview": true,
"qa_note_preserved_for_team_review": true
},
"public20_HTTP_repeated": false,
"new_publications": 0,
"QA_tests_repeated": false,
"new_roles_or_approvers": false,
"unresolved_originals": 2,
"inline_image_references_preserved": 16,
"existing_original_image_reused_by_exact_hash": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa",
"anchor_projects_and_empty_portfolio": "Separate failure; cause remains unconfirmed; not resolved by this delivery",
"user_review_of_commit_package_or_guide_claimed": false,
"private_delivery_executor_sha256": "308ac6e0c5deb99e4176af5891e8e084aa327864b460d5d523931c6e2379691c",
"private_verifier_sha256": "a669324009315e44271d5d536a08a4049e9e2ba68d1a3f52f3a0921e5a3a0fb1"
}
@@ -0,0 +1,95 @@
{
"date": "2026-10-23",
"status": "installed_in_homologation_delta_verified",
"target": "https://teste-investidor.tempo.co.mz",
"installed_commit": "55136b446deb710adf58ce2460e571bb19e94375",
"functional_commit": "4a01e9e2b7362282f844a8c6e0b26380ab0b71e0",
"qa_receipt_commit": "55136b446deb710adf58ce2460e571bb19e94375",
"diagnosis_commit": "ad4160f04074eb54ee3e5da213ba1cba884f02de",
"previous_served_commit": "d8224ba84b56482e0291bc18bb1c9c29316b19b9",
"integration": {
"mode": "normal fast-forward push to existing main; no force",
"concurrent_main_at_integration": "d8224ba84b56482e0291bc18bb1c9c29316b19b9",
"previous_history_preserved": true,
"schema_configuration_dependency_delta": false,
"fixtures_or_data_payload_shipped": false
},
"public_revision": {
"commit": "55136b446deb710adf58ce2460e571bb19e94375",
"branch": "main",
"built_at": "2026-10-23T15:55:10.921100+00:00",
"manifest_sha256": "b2dd6172516f220344ede5fc9d7876b7fc8417416d03842d737452709379a12c"
},
"installed_delta": {
"browser": "Edge; existing normal homologation session",
"existing_opportunity_id": 53,
"existing_reference": "INV-MZ-GEN-8326396601945337",
"existing_case": "958df138-7980-4685-8389-fa90be02ed6b",
"links": [
{
"href": "/investidor/terra-duat/novo?context=958df138-7980-4685-8389-fa90be02ed6b&opportunity=opportunity%3A53",
"text": "Terra/DUAT desta oportunidade"
},
{
"href": "/balcao-virtual/dossiers/958df138-7980-4685-8389-fa90be02ed6b?opportunity=opportunity%3A53",
"text": "Balcão desta oportunidade"
}
],
"authenticated_scoped_get": {
"case_id": "958df138-7980-4685-8389-fa90be02ed6b",
"version": 5,
"opportunities": [
{
"key": "opportunity:53",
"territory": "Sofala",
"park": null
}
],
"land": [
{
"province_id": "f57ff041-6275-57b9-8800-c35e46e0d475"
}
],
"desk_territory": "f57ff041-6275-57b9-8800-c35e46e0d475",
"submission_location": "Sofala",
"scoped_snapshot_choices": [
"opportunity:53"
]
},
"new_requests_created": 0,
"requests_saved_or_submitted": 0,
"accepted_business_cases_repeated": false,
"qa_proof_reused": "multi-opportunity-context-qa-receipt-20261023.json",
"installer": "Existing Git-triggered isolated build and atomic promotion; public served revision confirms completion. No manual edit of served directory."
},
"preservation": {
"protected_files_unchanged": {
"app/Services/InvestmentComplements.php": true,
"resources/js/components/investment-complements.tsx": true,
"docs/investmoz/unification/continuity-corrections-installation-receipt-20261023.json": true
},
"complements_delivery": "d8224ba8",
"previous_closes": [
"6b544548",
"4098192a"
],
"qa_fixtures_remain_qa_only": true,
"real_publication_states_not_changed": true
},
"deferred": [
"statistics",
"AI"
],
"limits": [
"Installed delta only; does not claim a new business acceptance or repeat second-opportunity QA cases.",
"No new native PDF acceptance.",
"Overall transversal implementation remains partial beyond this delivered scope."
],
"rollback": "Existing atomic pipeline rollback, retaining shared database, uploads, configuration and later history; prior served reference d8224ba8.",
"evidence_sha256": {
"installed-context-summary.json": "7f167ff8e13455eacf3a326aa0d0644870f849af33d0744e941f6afa9feefe9e",
"installed-links.json": "6de8a43461394870bb5b8f0462b13a71c29610c843d029d9bae32177849748f4",
"installed-scope.png": "04b1c7290305f8e2bafbfdd2fb77bb1d38207b3d30763dd3576e5aea154358aa",
"public-revision.json": "ffc2c30c02ced31797ed900f25dbb05c0274e2ff4a9cdb8bc0ef6ed7deddc653"
}
}
@@ -0,0 +1,118 @@
{
"date": "2026-10-23",
"status": "executable_correction_verified_in_isolated_qa_not_installed",
"functional_commit": "4a01e9e2b7362282f844a8c6e0b26380ab0b71e0",
"branch": "codex/opportunity-consumer-scope-20261023",
"diagnosis_commit": "ad4160f0",
"preserved_delivery": "d8224ba8",
"preserved_closes": [
"6b544548",
"4098192a"
],
"cause_and_correction": {
"parks": "Per-choice park reference and validation against that choice corridor and territory. Own lookup refreshes on own corridor/site. Previous common park is retained on its original first choice when reading legacy data.",
"consumer_scope": "Explicit opportunity key is resolved within the authorised dossier. Known fiche entry retains that key; multiple choices without a key show a chooser and propose only common data. No positional fallback for the three consumers.",
"persistence": "Canonical server-generated snapshot in each existing encrypted payload: identity, territory, corridor, park, source/version and scoped requirements. Client snapshots are replaced; foreign dossier identities are rejected.",
"fields": "Existing inheritance reconciliation retains deliberate local edits and parcel details. Returned desk request field restrictions retain locked association/snapshot.",
"reporting": "Land printable report displays saved identity and context; desk receipt displays saved scope; manifestation revision and intake metadata retain canonical association; common dossier report rows pair each park with its own opportunity."
},
"qa": {
"database": "simulator_fiscal_delta_20261021_test",
"port": 55441,
"browser": "Edge",
"url": "http://127.0.0.1:18574",
"actor_id": 509,
"new_case": "1017980b-a61c-44a4-b8d5-bd909c2d1316",
"first_choice_exactly_preserved": true,
"second_change": "309: Gaza/Limpopo/QA Gaza park -> Manica/Beira/QA Manica park",
"consumers": {
"land": {
"key": "opportunity:309",
"territory": "Manica",
"park": "SYNTHETIC QA — parque de ensaio Manica",
"context_revision": 2,
"requirement_keys": [
"opportunity:309"
]
},
"desk": {
"key": "opportunity:309",
"territory": "Manica",
"park": "SYNTHETIC QA — parque de ensaio Manica",
"context_revision": 2,
"requirement_keys": [
"opportunity:309"
]
},
"submission": {
"key": "opportunity:309",
"territory": "Manica",
"park": "SYNTHETIC QA — parque de ensaio Manica",
"context_revision": 2,
"requirement_keys": [
"opportunity:309"
]
}
},
"temporary_fixtures": "Three clearly marked synthetic parks only in isolated QA. Existing opportunity identities 286/309 reused; no new opportunity or service. Accepted complements case left intact."
},
"checks": {
"feature_tests": 4,
"feature_assertions": 104,
"frontend_tests": 3,
"typescript": "passed",
"build": "passed",
"browser": [
"second-only edit, park options, confirmation and recovery",
"land entry from identified second choice, save, recovery and printable report",
"desk scoped entry, draft save and reload",
"manifestation from existing fiche, explicit dossier binding, draft save and reload",
"unscoped multi-choice entry asks for a choice; selecting second recovers its Manica scope"
],
"additional_checks": [
"canonical snapshot rejects client forgery",
"foreign identity and other actor rejected",
"intermediate unknown territory draft recovered without restoring the source location",
"returned request preserves locked association",
"desk receipt template renders canonical second scope; manifestation revision retains association; report park rows retain both identities"
],
"limits": [
"No external submission or institutional decision was made. Desk receipt template was exercised with an isolated transaction fixture, not claimed as a real receipt.",
"Land HTML printable report was visually checked; no new native PDF export acceptance is claimed.",
"Accepted complement functional evidence was reused; no new visual round of those cases.",
"Initial test invoker used default arguments and started the default suite; it was interrupted and fixed to target this file. That incomplete run is not acceptance evidence."
]
},
"preservation": {
"protected_files_unchanged": {
"app/Services/InvestmentComplements.php": true,
"resources/js/components/investment-complements.tsx": true,
"docs/investmoz/unification/continuity-corrections-installation-receipt-20261023.json": true
},
"accepted_case_choices_and_sites": true,
"accepted_complements_retained": 4,
"migrations": 0,
"content_imports": 0,
"active_database_writes": 0,
"publication_changes_in_homologation": 0,
"qa_fixtures_shipped": false
},
"homologation": {
"unchanged": true,
"served_commit": "d8224ba84b56482e0291bc18bb1c9c29316b19b9",
"main_not_updated": true
},
"evidence_directory": "outputs/multi-opportunity-fix-20261023",
"evidence_sha256": {
"desk-recovered.txt": "a294e3d1a439e7ff42b66c59e9b4edc4a960a606384880faba02b78a4da884cc",
"explicit-choice-selected.txt": "f2a7432816b3b39881111ea7f4ccb2ac29a586ac407e5cd1a0f54f9b901cd359",
"explicit-choice.txt": "437ea9df20c437b8781c2279c8aa0f847a4dc45ffb1d3d0b455e4520b2c38297",
"land-report.png": "a586050af1f58d279fe2733f03861bf8f8d4e3f76e5076aee2e013a9ca7f4802",
"land-report.txt": "b274f72934d4717ffc08c75dc6574840b7f0b0ec0e6e987ebc52ce1fa56549e5",
"submission-recovered.png": "5dc5da5ff7bdb48a06649fd287e5c22f606e8bcc8035331821c0cd0c4cc0668e",
"submission-recovered.txt": "ac64e391cdaaa59a479e0ff3a829dc6daddf40d0d28b89a940cdfc35f223d6e1",
"verified-state.json": "a709caae0e6877128848dbb51d7bca929fd8457ace4331a565b422edef4e290f",
"visual-fixture.json": "6828f245430b938f0f6253186e81211af26641d959af0d7321d7c0d51bec5d55"
},
"transversal_status": "partial; this receipt covers the four named QA consumers only"
}
@@ -0,0 +1,122 @@
{
"date": "2026-10-23",
"status": "assessment_completed_gaps_confirmed_not_corrected",
"code_revision": "af9261809c6fece9ea3c7c79b49a80562f2ace68",
"accepted_complements_delivery": "d8224ba8",
"prior_closes": [
"6b544548",
"4098192a"
],
"accepted_cases_repeated": false,
"homologation_changed": false,
"probe": {
"date": "2026-10-23",
"qa_database": "simulator_fiscal_delta_20261021_test",
"qa_port": 55441,
"case_id": "6fdbb963-726e-4048-9884-8cddc04e77a6",
"transaction_rolled_back": true,
"first_choice_exactly_preserved": true,
"second_before": {
"key": "opportunity:309",
"territory": "Gaza",
"corridor": "limpopo"
},
"second_after_recovered": {
"key": "opportunity:309",
"territory": "Manica",
"corridor": "beira"
},
"third_choice_preserved": true,
"consumers": {
"land": {
"unchanged_after_second_change": true,
"before": {
"project_reference": "SYNTHETIC QA — conferência visual de complementos 23-10-2026",
"activity": "Agronegócio",
"purpose": "Produção, silos, moagem e processamento de milho, trigo e arroz.",
"parcels": [
{
"province_id": "f57ff041-6275-57b9-8800-c35e46e0d475"
}
]
},
"after": {
"project_reference": "SYNTHETIC QA — conferência visual de complementos 23-10-2026",
"activity": "Agronegócio",
"purpose": "Produção, silos, moagem e processamento de milho, trigo e arroz.",
"parcels": [
{
"province_id": "f57ff041-6275-57b9-8800-c35e46e0d475"
}
]
}
},
"desk": {
"unchanged_after_second_change": false,
"before": {
"title": "SYNTHETIC QA — conferência visual de complementos 23-10-2026",
"territory_id": "f57ff041-6275-57b9-8800-c35e46e0d475",
"location_source": "Referência consultada no contexto v2: Sofala. Não confirma disponibilidade.",
"description": "Produção, silos, moagem e processamento de milho, trigo e arroz.",
"sector": "Agronegócio",
"province_id": "f57ff041-6275-57b9-8800-c35e46e0d475"
},
"after": {
"title": "SYNTHETIC QA — conferência visual de complementos 23-10-2026",
"territory_id": "f57ff041-6275-57b9-8800-c35e46e0d475",
"location_source": "Referência consultada no contexto v3: Sofala. Não confirma disponibilidade.",
"description": "Produção, silos, moagem e processamento de milho, trigo e arroz.",
"sector": "Agronegócio",
"province_id": "f57ff041-6275-57b9-8800-c35e46e0d475"
}
},
"submission": {
"unchanged_after_second_change": true,
"before": {
"sector": "Agronegócio",
"objective": "Produção, silos, moagem e processamento de milho, trigo e arroz.",
"location": "Sofala"
},
"after": {
"sector": "Agronegócio",
"objective": "Produção, silos, moagem e processamento de milho, trigo e arroz.",
"location": "Sofala"
}
}
},
"parks": {
"ui_request_origin": "choices[0]",
"first_context": {
"corridor": "beira",
"territory": "Sofala"
},
"first_options": [],
"second_options_before": [],
"second_options_after": [],
"separate_park_slot_available": false
},
"revision_before": 2,
"revision_inside_transaction": 3,
"complement_acceptance_retested": false,
"original_case_exactly_preserved": true
},
"cause": {
"parks": "context.tsx derives request parameters and effect dependencies solely from choices[0]; selection.park and server compatibility validation are common and based on the first choice.",
"land": "InvestorContext::prefill generates one parcel from common fields; form.tsx applies only v[0] to the first parcel. No opportunity identity is attached to that proposal.",
"desk": "prefill uses common fields and first territory. A second-choice change updates the context revision text but retains first-choice territory and description.",
"submission": "prefill uses common sector, summary and location. No second-choice scope is supplied.",
"display": "DossierPrefill renders OpportunityChoices only for simulator; the other three consumers show common selection. Complement relations retain their independent existing display and are not retested."
},
"evidence_limits": [
"Commit/recovery exercised through existing application services inside a rolled-back QA transaction, not a new browser-session acceptance.",
"QA park option sets were empty for the sampled locations; no positive park availability or loss of a registered park is claimed.",
"Terra/DUAT current UI inspected read-only; desk and submission conclusions are based on executed prefill and source contracts, not submitted operations.",
"Deliberate form edits, save/recovery of the three destination forms and per-opportunity park selection remain acceptance criteria for a correction."
],
"visual_land": {
"path": "outputs/multi-opportunity-review-20261023/land-visible.txt",
"sha256": "5a2be5f9d0f6f0073d053bfdcb4523cf6e531ad3c929c2dbe6a8d4aaa34dfe4d"
},
"next_correction": "Scope zone selection and destination form proposals to an explicit opportunity identity; preserve other choices and deliberate edits; verify a second-only change and save/recovery in each destination.",
"transversal_status": "partial"
}
@@ -0,0 +1,63 @@
{
"date": "2026-10-23",
"candidate": "1afdfee8",
"native_report_fix": "2856240c",
"deployment_status": "installed_in_homologation_delta_verified",
"origin_main_checked": "55136b44",
"accepted_scopes_retested": false,
"new_test": "tests/frontend/native-pdf-scope.test.mjs: 1 passed",
"types": "passed",
"build": "passed",
"qa_report": {
"revision": 13,
"opportunity": "opportunity:309",
"location": "Manica",
"declared_funding_total": "123456 MZN",
"declared_secured": "45678 MZN",
"expected_gap": "77778.00 MZN",
"native_pdf_expected": "outputs/multi-opportunity-fix-20261023/candidata-1afdfee8.pdf",
"pdf_saved_verified": true,
"native_pdf_actual": "C:\\laragon\\www\\portal-investidor\\outputs\\multi-opportunity-fix-20261023\\Relatório — SYNTHETIC QA - escolha comum aos tres consumidores.pdf",
"native_pdf_sha256": "010eb4ea12015563c1b2f8bc255b1078c276f824c50690732541d5bffe34b8a6",
"native_pdf_pages": 10,
"visual_pagination": "passed pages1-10",
"file_discovery": {
"machine": "ONALA",
"user": "macbook",
"session": "Console",
"extension": ".pdf",
"size_bytes": 191637,
"requested_basename_not_found": "candidata-1afdfee8.pdf",
"actual_basename": "Relatório — SYNTHETIC QA - escolha comum aos tres consumidores.pdf",
"resolution": "existing file discovered by listing the exact requested directory; no rename, copy or PDF regeneration"
}
},
"anchor_analysis": {
"status": "analysed_not_implemented",
"confirmed_code_gaps": [
"sector filter absent from portfolio UI and query contract",
"no direct idea preparation from anchor fiche without an associated opportunity"
],
"directed_verification_remaining": [
"reconcile territorial data with demonstrated and destination state using equivalent access",
"anonymous interest authentication return and same-fiche identity"
],
"existing_accepted_branch": "portfolio origin via associated opportunity to dossier and simulator is preserved, not retested",
"qa_documentary_anchors": 15,
"qa_document_sector_location_region_present": true,
"qa_canonical_primary_territories": 0,
"qa_public_location_links": 0,
"qa_published_opportunity_links": 0,
"synthetic_anchor_excluded": true,
"inventory_sha256": "7d2c0c36fa51b05ce97dfd25c997b77eccf7136a99220ed8607fcd2436fd3d39",
"no_homologation_loss_inferred": true
},
"statistics_and_AI": "deferred",
"installation": {
"status": "installed_in_homologation_delta_verified",
"commit": "8036b9b071229dbf7e85ea748db70abcbeefc379",
"receipt": "treatment-scope-installation-receipt-20261023.json",
"delta_verified": true
},
"status": "installed_in_homologation_delta_verified"
}
@@ -0,0 +1,949 @@
{
"date": "2026-10-23",
"current_public_readonly_confirmation": {
"date": "2026-10-23",
"url": "https://teste-investidor.tempo.co.mz/carteira",
"visible_dossiers": 29,
"projects": 15,
"clusters": 8,
"corridors": 6,
"manifest": "approved-portfolio-visibility-20261023.json",
"missing_manifest_ids": [],
"different_titles": [],
"session_scope": "Authenticated browser reading the public route; prior anonymous recovery proof preserved",
"remote_data_writes": 0,
"remaining_continuity_gap": "from_dossier is validated on opportunity consultation but not carried by the Prepare investment link into the private context"
},
"operation": "Inventory before further acceptance tests; no active data changed",
"baseline_commit": "37a8eb015ac5022fd34bcd89cfe47c9fc61903b0",
"existing_43_components": [
{
"number": 1,
"component": "Documentos públicos",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Disponível: 29 recursos públicos; recibo editorial 22/10. Os 20 artigos conservam a aceitação anterior.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/documentos) · 29 fichas privadas; zero publicação adicional. Consulta pública do acervo permanece fechada."
},
{
"number": 2,
"component": "Ficha integral dos parques",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Disponibilizado localmente em 18/10; destino conserva 16 fichas, 8 publicadas. Recorte integral não é reaprovado por este inventário.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/zonas) · 16 fichas privadas/pendentes, 19 relações territoriais; publicação e gestão autorizada separadas."
},
{
"number": 3,
"component": "Navegação contextual e manifestação de interesse",
"historic_status": "Disponível",
"current_preservation_status": "Navegação/percurso preservados; carteira documental reposta em 23/10: 29 dossiers, incluindo 15 projectos âncora.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/investimentos/iniciar) · Início, contexto e navegação abrem; gravação/recuperação aceites em QA reutilizadas. Sem dossier real no destino."
},
{
"number": 4,
"component": "Referências económicas — lote mínimo",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Código e referências presentes; manter o âmbito da autorização anterior, sem inferir publicação de todas as opções.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/management/investment/references) · 11 actividades presentes; cinco novas referências transportadas. Activação/publicação do lote não executada."
},
{
"number": 5,
"component": "Sectores e duplicação visual",
"historic_status": "Disponível",
"current_preservation_status": "Entrega de 18/10 conservada no código/histórico; sem remoção constatada nesta leitura.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/sectores) · Ecrã aberto: dez sectores principais, especialização Fintech sem cartão principal duplicado; 12 registos conservados."
},
{
"number": 6,
"component": "Duplicados distintos — deteção e consolidação",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/management/sector-reconciliation) · Código presente; consolidação de registos distintos não demonstrada no destino."
},
{
"number": 7,
"component": "Fintech — enquadramento específico",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Fintech público no lote aceite; especialização preservada. Não reabrir a organização sectorial aprovada.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/sectores/mercado-financeiro/fintech) · Código instalado; artigo de origem permanece privado, sem publicação adicional."
},
{
"number": 8,
"component": "Legado — conteúdo restante",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "20 artigos públicos e 28 privados cadastrados no destino; dois originais incompletos. Recibo 37a8eb01.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/sectores) · Adaptador habilitado; artigos/imagens transportados privadamente, sem os publicar."
},
{
"number": 9,
"component": "Piloto agronegócio",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Piloto Agronegócio concluído: artigo e imagem preservados; não é uma oportunidade empresarial concreta.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/sectores) · Originais privados; seis oportunidades sintéticas excluídas. Nenhuma oportunidade real publicada."
},
{
"number": 10,
"component": "Percurso do investidor — 19 etapas",
"historic_status": "Disponível",
"current_preservation_status": "Percurso e preparação privada habilitados no destino; entrega adaptativa de 22/10. Zero dossiers pessoais não é perda de função.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/investimentos) · Início e lista própria abrem; definição de 19 etapas instalada. Não repetidas as 19 etapas aceites."
},
{
"number": 11,
"component": "Fiscal/contributivo e exportações",
"historic_status": "Não demonstrado",
"current_preservation_status": "Tesouraria/exportações e consumidor IVA têm entregas delimitadas em 21–22/10; restantes variantes não promovidas a concluídas.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Formulário aberto; cenários fiscais/contributivos/exportação e parâmetros efectivos não certificados nesta execução."
},
{
"number": 12,
"component": "Negócios existentes e classificação",
"historic_status": "Disponível",
"current_preservation_status": "Módulo habilitado e fluxos conservados. Ausência de anúncios não demonstra desaparecimento de código.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/negocios) · Catálogo abre e módulo habilitado; zero negócios copiados. Gestão própria sujeita às permissões existentes."
},
{
"number": 13,
"component": "Negócios similares",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/negocios) · Código instalado; comparação com negócios reais não demonstrada."
},
{
"number": 14,
"component": "Importação de empresas/empreendimentos",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/management/negocios) · Mecanismo presente; nenhuma empresa/transacção local ou QA transferida. Operador de gestão por definir."
},
{
"number": 15,
"component": "Fundos",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; critérios específicos de fundos não demonstrados no destino."
},
{
"number": 16,
"component": "Comparador societário e modelos",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; comparação societária/modelos não demonstrados nesta execução."
},
{
"number": 17,
"component": "Capitalização/private equity e Bolsa",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; cenários específicos de capitalização/Bolsa não demonstrados no destino."
},
{
"number": 18,
"component": "Checklists versionadas",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/servicos) · Definições presentes; checklist aplicada a dossier legítimo continua por demonstrar."
},
{
"number": 19,
"component": "Indicadores de utilização",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/dashboard) · Código presente; indicadores com dados reais e permissões adequadas não demonstrados."
},
{
"number": 20,
"component": "Jornadas educativas",
"historic_status": "Disponível",
"current_preservation_status": "Jornadas habilitadas; cartões educativos disponibilizados. Mantido o âmbito parcial da educação.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/educacao) · Área abre e flag habilitada; nenhum percurso educativo completo foi repetido."
},
{
"number": 21,
"component": "Partilha documental individual e variantes de sala",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/parcerias/painel) · Estruturas de salas/partilha instaladas; participantes, consentimentos e partilha real não demonstrados."
},
{
"number": 22,
"component": "Restauro, modalidades e SLA",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Backup/restauro coberto separadamente; modalidades/SLA não homologados. Escalonamento continua desligado como local."
},
{
"number": 23,
"component": "Cartografia e consumidores territoriais restantes",
"historic_status": "Não demonstrado",
"current_preservation_status": "Demarcação reposta: 408 geometrias. Polígonos de localidades e correspondências já pendentes não são nova regressão.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Catálogos/mapas abrem. 1 921 referências geográficas no alvo versus 1 990 locais; diferença não reconciliada por item fora do manifesto."
},
{
"number": 24,
"component": "Acessibilidade e restantes consumidores",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Mensagens R2 preservadas; não se declara certificação integral de acessibilidade de todos os módulos."
},
{
"number": 25,
"component": "Fechos de operação/avaliação documental",
"historic_status": "Não demonstrado",
"current_preservation_status": "Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Sem processo operacional legítimo para demonstrar fecho/avaliação documental."
},
{
"number": 26,
"component": "Conta simples, login e MFA privilegiado",
"historic_status": "Disponível",
"current_preservation_status": "Login/MFA e designações reais documentados; sem alteração de papéis nesta passagem.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/login) · Login e desafio MFA reais concluídos; conta simples R2 e controlos privilegiados preservados."
},
{
"number": 27,
"component": "Manifestação simples, recibo e acompanhamento do interessado — R2",
"historic_status": "Disponível",
"current_preservation_status": "R2 e percursos posteriores conservados; reutilizar as provas aceites.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/investidor/manifestacoes) · Recorte R2 conservado, com provas aceites reutilizadas; nenhuma manifestação fictícia criada."
},
{
"number": 28,
"component": "Pesquisa — estados do recorte R2",
"historic_status": "Disponível",
"current_preservation_status": "Pesquisa e selectores corrigidos em 22/10; a carteira vazia é tratada como perda separada.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Catálogos abrem; quatro estados e protecção contra respostas antigas preservados do R2, sem repetir ensaios aceites."
},
{
"number": 29,
"component": "Biblioteca privada própria, scanner e recuperação — DOC01",
"historic_status": "Disponível",
"current_preservation_status": "Biblioteca, scan e recuperação disponíveis: 59 recursos, 29 públicos e 30 privados.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/management/portal-services) · Conta de custódia vê 30 ficheiros limpos. Novo original recuperado no navegador com SHA-256 igual à fonte; sem gestão global."
},
{
"number": 30,
"component": "Backup/restauro e reversão do recorte R2 + DOC01",
"historic_status": "Disponível",
"current_preservation_status": "Backup/restauro 22/10 documentado: 174 tabelas. Reposição activa terá backup próprio antes da escrita.",
"evidence": "Backup fresco consistente: 144 tabelas restauradas iguais, 34 ficheiros/configuração conferidos, recuperação portável e metadados Linux/PG protegidos. Reversão de apresentação preparada."
},
{
"number": 31,
"component": "Balcão Virtual",
"historic_status": "Disponível",
"current_preservation_status": "Balcão e definições instalados; zero casos reais no destino não é perda de funcionalidade.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Painel e serviço existente abrem; dez tabelas base instaladas, definição interna project-preparation v5 transportada."
},
{
"number": 32,
"component": "Vínculo positivo e recuperação do dossier",
"historic_status": "Não demonstrado",
"current_preservation_status": "Recuperação de contexto demonstrada em QA nas entregas posteriores; ausência de caso real não reabre a prova aceite.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/investimentos) · Vínculo documental positivo pendente: zero dossiers da conta no destino. Não foram criados dados fictícios."
},
{
"number": 33,
"component": "Cadastro canónico e recursos de oportunidade",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Cadastro e recursos disponibilizados; 30 oportunidades com resumos em preparação publicados.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/management/opportunities) · Editor/schema instalados; zero oportunidades. Gestão/revisão exigem atribuições existentes ainda não configuradas."
},
{
"number": 34,
"component": "Perfis, revisão independente e publicação",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Perfis e atribuições existentes preservados; inventário não cria decisão organizacional nova.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/perfis/gestao) · Schema e fluxos presentes; zero atribuições/titulares. Nenhum papel ou privilégio concedido automaticamente."
},
{
"number": 35,
"component": "Bolsa de parcerias e salas internas",
"historic_status": "Disponível",
"current_preservation_status": "Bolsa e percursos existentes preservados; variantes com participantes reais mantêm saldo próprio.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/parcerias) · Catálogo e mapa abrem, nove tabelas de relações/salas instaladas; colaboração com participantes reais não demonstrada."
},
{
"number": 36,
"component": "Onde se localiza / associações territoriais e produtivas",
"historic_status": "Ausente",
"current_preservation_status": "Estado Ausente superado: integração territorial entregue em 20–22/10; catálogos e 408 limites conservados.",
"evidence": "Harmonização territorial apenas QA, expressamente excluída da revisão autorizada."
},
{
"number": 37,
"component": "Lote privado Inhambane —30 fichas",
"historic_status": "Ausente",
"current_preservation_status": "Estado Ausente superado: 30 fichas Inhambane transportadas e resumos divulgados em 22/10.",
"evidence": "30 fichas Inhambane apenas QA, não importadas nem publicadas no destino."
},
{
"number": 38,
"component": "Enquadramento das12 fichas de programas/carteira",
"historic_status": "Ausente",
"current_preservation_status": "Estado Ausente superado: enquadramento de 11 programas e uma carteira incluído na entrega posterior.",
"evidence": "Enquadramento das 12 fichas permanece no incremento QA separado."
},
{
"number": 39,
"component": "Originais partilhados e referência documental no dossier — Inhambane",
"historic_status": "Ausente",
"current_preservation_status": "Estado Ausente superado: originais privados/referências e contexto incluídos na entrega posterior; não reimportar.",
"evidence": "Reutilização documental Inhambane/dossier permanece no incremento QA separado."
},
{
"number": 40,
"component": "Orientação assistida/IA",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Sem regressão demonstrada: integração externa não fechada; não inventar credencial nem activar fornecedor.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/management/ai) · Código presente; integração desligada. Credencial própria do destino/chamada externa não confirmadas."
},
{
"number": 41,
"component": "Integração externa SuiteCRM",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Desabilitada também no local por opção anterior; não desapareceu por efeito da entrega editorial.",
"evidence": "Permanece desligada como na versão local; nenhuma credencial ou envio externo copiado/executado."
},
{
"number": 42,
"component": "Operação administrativa e resposta da equipa na homologação",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Estado antigo superado: conta real e gestão autorizada demonstradas nas entregas posteriores. Sem novos papéis.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/management/interests) · Nenhuma conta com gestão global/atribuições operacionais. Custódia documental não autoriza resposta/revisão da equipa."
},
{
"number": 43,
"component": "Gestão editorial da biblioteca",
"historic_status": "Instalado mas desabilitado",
"current_preservation_status": "Gestão autónoma entregue; edição, prévia, gravação e recuperação dos 28 privados demonstradas no destino.",
"evidence": "[Abrir](https://teste-investidor.tempo.co.mz/management/portal-services) · Schema editorial instalado; publicação/edição pública não concedida à conta de custódia."
}
],
"existing_executable_surfaces": [
{
"surface": "------------------------------",
"component": "-------------------------------------------",
"routes": "-------------------------------------------------------------------------",
"accepted_evidence": "---------------------------------------------------------",
"code_files": []
},
{
"surface": "Início",
"component": "`PortalInvestidor`",
"routes": "`/`, APIs existentes de homepage",
"accepted_evidence": "`PublicPortalNavigationTest`, navegador",
"code_files": []
},
{
"surface": "Catálogo / Educação",
"component": "`investment/catalogue`",
"routes": "`/oportunidades`, `/educacao`; `/api/v1/investment/search`",
"accepted_evidence": "`InvestmentUnificationTest`",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\catalogue.tsx",
"exists": true
}
]
},
{
"surface": "Cluster / Corredor / Âncora",
"component": "`portfolio/index`, `portfolio/show`",
"routes": "`/clusters`, `/corredores`, `/projectos-ancora`, `/carteira/{id}`",
"accepted_evidence": "`InvestmentPortfolioTest`, testes territoriais",
"code_files": [
{
"path": "resources\\js\\pages\\portfolio\\index.tsx",
"exists": true
},
{
"path": "resources\\js\\pages\\portfolio\\show.tsx",
"exists": true
}
]
},
{
"surface": "Ficha",
"component": "`investment/detail`",
"routes": "`/oportunidades/{slug}`; `/api/v1/investment/opportunities/{slug}`",
"accepted_evidence": "`PublicPortalNavigationTest`, `InvestmentUnificationTest`",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\detail.tsx",
"exists": true
}
]
},
{
"surface": "Mapa",
"component": "Componente `Map` em catálogo",
"routes": "`/api/v1/investment/map`",
"accepted_evidence": "Teste PostGIS e estados vazios",
"code_files": []
},
{
"surface": "Comparar / exportar / sugerir",
"component": "`investment/catalogue`",
"routes": "`/compare`, `/export`, `/suggest` sob prefixo da API",
"accepted_evidence": "Teste de publicação e fórmula CSV",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\catalogue.tsx",
"exists": true
}
]
},
{
"surface": "Área do investidor",
"component": "`investment/workspace`",
"routes": "`/investidor/manifestacoes`; `/api/v1/investment/submissions`",
"accepted_evidence": "Isolamento por dono",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\workspace.tsx",
"exists": true
}
]
},
{
"surface": "Perfil / empresas",
"component": "`investment/profile`",
"routes": "`/investidor/profile`; API `/profile`, `/companies/{id?}`",
"accepted_evidence": "`InvestmentWorkspaceTest` e navegador",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\profile.tsx",
"exists": true
}
]
},
{
"surface": "Conversa / versões",
"component": "`investment/conversation`",
"routes": "API `/submissions/{id}/messages`, `/submissions/{id}/revisions/{version}`",
"accepted_evidence": "Testes de isolamento, encriptação e reenvio; navegador",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\conversation.tsx",
"exists": true
}
]
},
{
"surface": "Notificações",
"component": "`investment/notifications`",
"routes": "`/investidor/notifications`; API `/notifications`, `/{id}/read`",
"accepted_evidence": "Testes de mudança de estado e leitura; navegador",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\notifications.tsx",
"exists": true
}
]
},
{
"surface": "Manifestação",
"component": "`investment/submission`",
"routes": "`PUT /submissions/{id}`, `POST /submissions/{id}/submit`",
"accepted_evidence": "Testes de conflito, validação e reenvio",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\submission.tsx",
"exists": true
}
]
},
{
"surface": "Anexos",
"component": "Etapa 4 da manifestação",
"routes": "`/submissions/{id}/attachments`",
"accepted_evidence": "Teste de anexo privado e adulteração",
"code_files": []
},
{
"surface": "Contexto da oportunidade",
"component": "`investment/context`",
"routes": "`/management/opportunities/{id}/context`",
"accepted_evidence": "Teste de gestão, conflito e educação",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\context.tsx",
"exists": true
}
]
},
{
"surface": "Referências e formulários",
"component": "`investment/references`",
"routes": "`/management/investment/references`",
"accepted_evidence": "Teste de referências e reexecução do seed",
"code_files": [
{
"path": "resources\\js\\pages\\investment\\references.tsx",
"exists": true
}
]
},
{
"surface": "Atendimento / CRM",
"component": "`management/interest`, gestão CRM existente",
"routes": "`/management/interests/{id}`",
"accepted_evidence": "Testes de intake e CRM existentes",
"code_files": [
{
"path": "resources\\js\\pages\\management\\interest.tsx",
"exists": true
}
]
},
{
"surface": "Apoio / documentos / parcerias",
"component": "`portal/services`, `portal/manage`",
"routes": "`/apoio`, `/documentos`, `/parcerias`, `/balcao-virtual`",
"accepted_evidence": "`PublicPortalServicesTest`",
"code_files": [
{
"path": "resources\\js\\pages\\portal\\services.tsx",
"exists": true
},
{
"path": "resources\\js\\pages\\portal\\manage.tsx",
"exists": true
}
]
}
],
"additional_approved_increment": "Concursos e anúncios: instalado e habilitado em 22/10; gestão autorizada e consulta, sem anúncio real publicado.",
"source_inventory_sha256": {
"local": "135bdc35b23d05e239f9c44ef9762af11111c880b4ef78faaacb63ebeb9dfdc2",
"qa": "632a488d2a0a7841babceb2242f6be61d534b54b64958db0d639185aabb80104",
"homologation": "a247f521b512654206a1fbf4fde03a46233d8e2d98122d119417bc0a2f70df23"
},
"confirmed_loss": {
"portfolio_and_anchor_visibility": {
"expected_published": 29,
"current_target_published": 0,
"records_present": 29,
"deletion_claimed": false,
"local_QA_content_source_matches": 29,
"target_hash_match": 29,
"recovery_status": "restored in QA and homologation",
"target_visible_after": 29
}
},
"historic_change": {
"commit": "e65cb26901a5ca18862134b20e4c17b1f1003ec2",
"date": "2026-10-06",
"change": "Removed portfolio:import --approval and document_owner publication path; import now creates drafts",
"does_not_prove_date_of_disappearance_from_particular_address": true
},
"qa_tests_run": false,
"facts_before_recovery": {
"local": {
"dossiers": 29,
"portfolio_visible": 29,
"projects": 16,
"clusters": 8,
"corridors": 6,
"resources": 29,
"geometries": 0
},
"qa": {
"dossiers": 29,
"portfolio_visible": 0,
"projects": 16,
"clusters": 8,
"corridors": 6,
"resources": 61,
"geometries": 408
},
"homologation": {
"dossiers": 29,
"portfolio_visible": 0,
"projects": 16,
"clusters": 8,
"corridors": 6,
"resources": 59,
"geometries": 408
}
},
"inventory_data_writes": 0,
"qa_recovery": {
"database": {
"name": "simulator_fiscal_delta_20261021_test",
"port": 55441
},
"qa": true,
"mode": "applied",
"manifest_sha256": "8d07007a2e1e8758d4741b4ac594f78d5943f71ad78b2daae7f85aefa9d2de4a",
"preview_sha256": "fbb9ba70df67ef349cb673a683f051f21a8ae62d192f09fc35f2abac7c45b35d",
"scope": 29,
"restored": 29,
"visible": 29,
"data_content_changed": false,
"new_dossiers_imported": 0,
"fake_editor_or_reviewer_assigned": false,
"plan": [
{
"id": "9871a8c0-d562-420b-a8f7-174c95db93f0",
"code": "A01",
"version": 1,
"status": "draft",
"content_sha256": "2adcbe741777d4352c47eb31281dc6b2e2d04903fcfd3e59778ddff4e1342977",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "64b824a0-3527-4024-a1a2-5234f5b4caaa",
"code": "A02",
"version": 1,
"status": "draft",
"content_sha256": "3ad808813909f1475f44a5fcf2a4e50c5d01e25ad8ea39d906c52519532cf0ee",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "6c03e980-c455-49cc-a3be-99d29014e91d",
"code": "A03",
"version": 1,
"status": "draft",
"content_sha256": "75266ff89da58e4d6e5422e49e4641c62dae4f171fa358b70e210c5883a1fd07",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "8bd83cc4-b36c-46cd-9287-b47e1162b578",
"code": "A04",
"version": 1,
"status": "draft",
"content_sha256": "f6a63e2ee2f2e7c98f1030d206996ad57b0d92129d021ed3c6bb53f161d56f5b",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "d8a88bac-4684-43fa-aea6-e54fef9b8a47",
"code": "A05",
"version": 1,
"status": "draft",
"content_sha256": "ea0f6b7843289d81dc2852327432721f8677e28ad8b41d754abc8c91307303d3",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "57a4ee7a-4c07-4e26-b8f5-e07726e41512",
"code": "A06",
"version": 1,
"status": "draft",
"content_sha256": "c7fae7b2ca541e734e587589dace8dc85dee2494876de6e30ecd91a7177a985d",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "70ae731e-fdef-473f-b495-14f319b28d60",
"code": "A07",
"version": 1,
"status": "draft",
"content_sha256": "3eb01ce3dd2cc68b43b816708f7e5f990c48f1aea6555cdfd169a286e11b9bd4",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "b30ecd8c-220d-4c7a-b1a7-704dc24d0933",
"code": "A08",
"version": 1,
"status": "draft",
"content_sha256": "bca089a4bb2601ca34c50e2c48068b4c2f9fa7a873955cf0587f261a83c2a146",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "dc8892ff-fdda-480c-81f1-2ffe0bfcb40b",
"code": "A09",
"version": 1,
"status": "draft",
"content_sha256": "7e3bb071f65e49245fbfd070f4123ac49985004e865c475bf7e34c85cc708aea",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "77b685f0-848f-4800-83f8-a29539ed8f31",
"code": "A10",
"version": 1,
"status": "draft",
"content_sha256": "02dc0fb152908e81b11304f6bc0c2252805ebd2ad1860b1a6bfa918e2cbcd5bb",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "cb125f84-6f75-48e5-a0de-2f5e6bd537ed",
"code": "A11",
"version": 1,
"status": "draft",
"content_sha256": "736fc41d941740f41697c579ffe6c4208d45005d7177b7c42f006554a5dde480",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "71b96599-09c2-4809-b98f-acbf0c2b6f38",
"code": "A12",
"version": 1,
"status": "draft",
"content_sha256": "9dbdb468b8644e07fabdc943ecde2f11ea561d5174c00559228ed29b2a29b1fe",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "5ce2d415-1c84-4625-a4cd-0f6738454318",
"code": "A13",
"version": 1,
"status": "draft",
"content_sha256": "7a62afdda8e2c3fcc47b1c9032359906ff75dff9b5d56c71ba7d88781a8f8620",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "88111fb4-ab20-4393-bd1b-3a1694fb4ac4",
"code": "A14",
"version": 1,
"status": "draft",
"content_sha256": "930836392126133499fa69313a4718ce2ec1f6c8f0839c184e9b5cc229460d1c",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "88e37c63-e785-446f-9074-84b000bffa59",
"code": "A15",
"version": 1,
"status": "draft",
"content_sha256": "d19d27451296b9816351e2d340f2aaff9fabf9732fb580d179e1c69ceb9fc512",
"source_sha256": "4919cd8aae6230e576947b38e5ff839b2661166fb2a8598bea1d662f15bd170a",
"already_restored": false
},
{
"id": "0c63d8de-18fd-451f-a7ca-1ea04dd5906c",
"code": "CLU-01",
"version": 1,
"status": "draft",
"content_sha256": "ccd16f969474dcc23b2ddf27eb4cf3ca090f22e418e0e8f33499f349e884d042",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"already_restored": false
},
{
"id": "fc1355f5-1473-4fe2-b3e2-b32d1f0b1042",
"code": "CLU-02",
"version": 1,
"status": "draft",
"content_sha256": "78c50659068301be0db94e8f4c9e77a20a4a052639dc4553291ff3568d3c0c4a",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"already_restored": false
},
{
"id": "99bf7c4c-e23b-47d5-8490-53082384043f",
"code": "CLU-03",
"version": 1,
"status": "draft",
"content_sha256": "99af7bb35b6769d84bee2175427b3a03027fea65a91dd894338fd7aa72db3820",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"already_restored": false
},
{
"id": "816b7c26-827b-475a-bf98-647b46afe88b",
"code": "CLU-04",
"version": 1,
"status": "draft",
"content_sha256": "a75f532e2464dadd13dd2ff815d99acfb2f05d3a0264fcf82dfe8e3db7db3a1f",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"already_restored": false
},
{
"id": "8eb7da82-1f1e-442b-9b1e-57b948c3f057",
"code": "CLU-05",
"version": 1,
"status": "draft",
"content_sha256": "fe2f37be8e3f20ccc35da91aa86872b960e9820f2e0de84200c157072b3c7b11",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"already_restored": false
},
{
"id": "65d709f5-20ed-4beb-8cf1-4a7fbd459e8f",
"code": "CLU-06",
"version": 1,
"status": "draft",
"content_sha256": "097498df28f63b614914cc171e4d58c6154442d0d6625185b3759878829528de",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"already_restored": false
},
{
"id": "a795cde3-3cfe-456b-ba8d-7ff3730024ec",
"code": "CLU-07",
"version": 1,
"status": "draft",
"content_sha256": "6c5407281d18069e05b84e1e7997d07fd79680e2b0e2a5b0e7d86cbfc3c2ca35",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"already_restored": false
},
{
"id": "05dc87a8-e1ab-41c9-a312-5b14b8ff16ce",
"code": "CLU-08",
"version": 1,
"status": "draft",
"content_sha256": "f59505944a91a849c27c16455001ad4a1d39aa58e59e8d5403cb0dc197c2faf8",
"source_sha256": "70688ba4623298f5ec073331c06c28ca5b0a9c552578e22ccebe20c27d1ca8cf",
"already_restored": false
},
{
"id": "986af8c5-2f90-40ab-a62a-4c37a5f42e30",
"code": "COR-01",
"version": 1,
"status": "draft",
"content_sha256": "26db6621cae0e76d2871f56926062bc0be5bb41e21521d2536655e809b6dff20",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"already_restored": false
},
{
"id": "a7271336-808b-42b4-8bfd-5b49c72dbe2b",
"code": "COR-02",
"version": 1,
"status": "draft",
"content_sha256": "0e6b80849fcb0746aa1a53133ee0d5ccc3df8c847243a1134922247ce3d9e1b8",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"already_restored": false
},
{
"id": "d9ccddb5-e6aa-4347-a481-37b694013ac1",
"code": "COR-03",
"version": 1,
"status": "draft",
"content_sha256": "64c8eba636661e6ebb56e2dd8dd6e978c405dd96c0c7b46bdd62c805018a6c4a",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"already_restored": false
},
{
"id": "ae26d529-5341-4b37-ad08-662c9d09a0cf",
"code": "COR-04",
"version": 1,
"status": "draft",
"content_sha256": "ce5b89974e1d2937e4901ab5603245cf2465afbe0a44a224c43f726069cdabd8",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"already_restored": false
},
{
"id": "69767dcc-4c22-4046-8f81-3797bc44572e",
"code": "COR-05",
"version": 1,
"status": "draft",
"content_sha256": "343ba56efeef5514f531cf61126596a8c65f882656da5f95390eb661a0810a7c",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"already_restored": false
},
{
"id": "fb85ebfc-798c-42df-a267-d4b771c443f8",
"code": "COR-06",
"version": 1,
"status": "draft",
"content_sha256": "465ff9717def3bad42efb25d1ffa04812f871e8b167adcbb20f68f5ef9460b57",
"source_sha256": "09a6a7664f6a910dbc22190ab85d832f5405dd3eaf19e712f313c4241b873b55",
"already_restored": false
}
]
},
"destination_recovery": {
"database": {
"name": "teste_investidor",
"port": 5432
},
"qa": false,
"mode": "applied",
"manifest_sha256": "8d07007a2e1e8758d4741b4ac594f78d5943f71ad78b2daae7f85aefa9d2de4a",
"preview_sha256": "6302eee1c69b44e726fc1e0b2c5a63ac0d5eb76b23eaf97295475ab411c1151e",
"scope": 29,
"restored": 29,
"visible": 29,
"data_content_changed": false,
"new_dossiers_imported": 0,
"fake_editor_or_reviewer_assigned": false
},
"preservation_after": {
"changed_tables": [
"audit_events",
"investment_dossiers",
"sessions"
],
"expected_recovery_changes": [
"investment_dossiers",
"audit_events"
],
"unchanged_tables": 171,
"table_count": 174,
"portal_resources_unchanged": true,
"documents_unchanged": true,
"geometries_unchanged": true,
"role_assignments_unchanged": true,
"new_dossiers": 0,
"deployment_watcher": "active"
},
"backup": {
"stage": "/var/backups/investmoz-preservation-inventory-20261023/backup",
"target": {
"database": "teste_investidor",
"port": 5432
},
"tables": 174,
"files_verified": 369,
"dump_sha256": "7b7a3a082e1259c53c321617eb84d10b544e5a211f5f708199026c4205d1c511",
"files_sha256": "0cc28141a081ce9d097735c7815b46f55d99300adf7361d936b857a24f897b86",
"restore_verified": false
},
"restore": {
"restore_verified": true,
"tables_verified": 174,
"container_network": "none",
"database": "preservation_restore_20261023",
"port": 55441,
"source_dump_sha256": "7b7a3a082e1259c53c321617eb84d10b544e5a211f5f708199026c4205d1c511",
"active_database_writes": 0
},
"public_recovery": {
"checked_utc": "2026-10-23T07:12:24.984213+00:00",
"results": [
{
"url": "https://teste-investidor.tempo.co.mz/carteira",
"http": 200,
"component": "portfolio/index",
"total": 29,
"authenticated": false
},
{
"url": "https://teste-investidor.tempo.co.mz/carteira?kind=project",
"http": 200,
"component": "portfolio/index",
"total": 15,
"authenticated": false
},
{
"url": "https://teste-investidor.tempo.co.mz/carteira?kind=cluster",
"http": 200,
"component": "portfolio/index",
"total": 8,
"authenticated": false
},
{
"url": "https://teste-investidor.tempo.co.mz/carteira?kind=corridor",
"http": 200,
"component": "portfolio/index",
"total": 6,
"authenticated": false
}
]
},
"new_contents_imported": 0,
"executed_recovery_source_sha256": "f4f76fa8b41bb5cf241093709ea8044592a54f8bafd0df9ca15b0c8745c45bcc",
"active_recovery_changes": {
"investment_dossiers": 29,
"audit_events": 29,
"credentials_stored": false,
"new_roles_or_approvers": false,
"new_content_approval": false
},
"qa_recovery_preparation_note": "An initial QA audit-key collision rolled back atomically. Audit identity was scoped per original before the successful QA and destination recovery; no active attempt used the faulty key."
}
@@ -0,0 +1,50 @@
{
"functional_candidate": "1bcf34c0",
"installation_procedure": "e46f9731",
"installed_revision": "f4d0970917e7e8d141f74c136b66138b929141fc",
"activated_utc": "2026-10-22T20:51:27.874333+00:00",
"counts": {
"tenders": 1,
"versions": 1,
"published_versions": 0,
"watches": 0
},
"verification_draft": {
"id": "b970f0f0-9257-42e1-8365-3358237b55db",
"created_by": 15,
"version": 1,
"published_version_id": null
},
"verification": {
"real_account_id": 15,
"mfa_renewed_by_user": true,
"management_hub": true,
"draft_saved_recovered": true,
"preview_opened": true,
"publication_performed": false,
"qa_proofs_reused": "8 tests / 285 assertions; 6 navigation tests; build and TypeScript"
},
"links": {
"management": "https://teste-investidor.tempo.co.mz/gestao-conteudos",
"catalogue": "https://teste-investidor.tempo.co.mz/concursos"
},
"backup": {
"database_sha256": "69de817b288ecb25db4b341375475f2408a47642a8ba69d338b217dcc982854f",
"files_sha256": "7b75c22e730b5600d6e97e1c6dbb4cd93982bdd47e9dd2a63de2359974132674",
"tables_restored_verified": 171,
"files_archive_verified": 341
},
"preservation": {
"protected_tables_unchanged": [
"users",
"portal_role_assignments",
"documents",
"portal_resources",
"opportunities",
"geo_locations",
"geo_geometries"
],
"original_files_preserved": 340
},
"private_document_anonymous_status": 404
}
@@ -1,5 +1,522 @@
# Rastreabilidade, utilização e formação
## Projectos âncora — entregues na homologação em cf2ddf21, 23/10/2026
Reconciliado o saldo de `6985f82e` com as provas territoriais de 7 de Outubro e os recibos de reposição. A leitura autenticada da homologação, com o papel da aplicação e transacção apenas de leitura, confirmou **29 fichas publicadas, 13 relações de clusters e zero ligações em `project_locations`**. As cinco tabelas comparadas não usam RLS. Os projectos transportados têm identificadores diferentes dos originais e perderam as 28 ligações territoriais e os respectivos metadados de citação; não desapareceram as 29 fichas. A reposição de visibilidade anterior não abrangia estas relações. A comparação delimita a omissão no transporte; não identifica por si só o instante em que ocorreu.
O [manifesto das 28 relações aceites](approved-anchor-territories-20261023.json) conserva identidades históricas, fontes, citações e papéis primary/secondary/impact, mapeando os projectos e dossiers actuais por identidade documental verificada. A ferramenta `tools/restore-approved-anchor-territories.php` confere hashes, versões, território e prévia; recusa conflitos e exemplares arquivados/restritos; preserva metadados concorrentes, guarda estado anterior e produz auditoria. Não publica fichas nem cria ofertas. Em QA foram repostas 28 relações de 13 projectos; a repetição foi demonstrada sem duplicação. A01/A02 continuam sem implantação individual inventada; Norte da Zambézia conserva o papel de impacto parcial.
Incrementos: pesquisa explícita pelo sector documental em conjunto com a localização; entrada directa da ficha pública para preparar uma ideia, mesmo sem oportunidade publicada; origem `portfolio:<id>` guardada no dossier e no snapshot/relatório do simulador. A cobertura documental não é atribuída como implantação. A localização explicitamente escolhida na pesquisa acompanha a entrada; uma localização já guardada conserva-se, com divergência explicada. Títulos e dados deliberadamente editados mantêm-se na comparação existente. A retoma autenticada da manifestação conserva a mesma ficha e os filtros e não cria pedidos por GET. A preparação iniciada pela manifestação usa a associação já existente ao dossier, com controlo de proprietário e versões; não submete o pedido.
Provas dirigidas: `InvestmentAnchorIntegrationTest` (4 casos, 116 asserções) e `anchor-integration.test.mjs` (2 casos); ver [recibo de QA e reconciliação](anchor-integration-receipt-20261023.json). Mantidos os percursos já aceites de carteira → oportunidade associada → dossier/simulador, complementos `d8224ba8`, consumidores `55136b44` e tratamento `8036b9b0`/recibo `cdf8fcfb`. Nenhuma nova impressão de PDF, prova Word/ZIP ou ronda dos fechos aceites. Estatísticas e IA permanecem adiadas.
**Entregue e delta conferido** na revisão `cf2ddf21a1457f4852fe6aa10069ec3b854f1567`, após integração `3fd5128c` e correcção delimitada da verificação de finais de linha do manifesto entre Windows e Git. A primeira prévia recusou o hash divergente antes de qualquer escrita; a prévia canónica passou. A reposição autenticada recuperou **28 relações em 13 projectos**, com 13 eventos de auditoria, zero novas fichas e zero alterações de publicação. As **29 identidades aprovadas, conteúdos e fontes** permanecem exactamente iguais; preservadas as 13 relações de clusters e os corredores. A comparação das 174 tabelas encontrou 169 iguais por contagem/hash; as diferenças de domínio limitam-se a projectos/metadados, relações territoriais e auditoria; cache e sessões tiveram alterações técnicas. O backup restaurou 174 tabelas iguais numa base isolada sem rede e verificou 369 ficheiros.
Os 15 ficheiros funcionais instalados coincidem com o Git. Instalador: zero migrações e zero importações automáticas; controlos habituais aprovados (contrato 1/3106, PHP 57/309 e frontend 216/216). Conferência pública apenas do delta: pesquisa A06 por sector documental e Sofala encontra a ficha original; quatro localizações e respectivas fontes recuperadas; retorno conserva filtros; a entrada para preparar ideia usa `portfolio:<id>` e a província escolhida. Nenhum dossier, pedido ou manifestação de ensaio foi criado na homologação. Os ensaios sintéticos continuam exclusivamente em QA.
**Saldo técnico deste recorte: sem pendências de código, reposição ou disponibilização identificadas.** Segue a avaliação final de navegação e apresentação pela Tempo. Este fecho não declara implementadas funcionalidades futuras nem a conclusão integral da aplicação transversal; Estatísticas e IA continuam adiadas. O inventário e as provas anteriores permanecem preservados, sem nova ronda dos casos aceites.
## Oportunidade em tratamento — entregue na homologação em 8036b9b0, 23/10/2026
Concluída a disponibilização autorizada da candidata **`1afdfee8`**, incluindo a correcção delimitada do relatório **`2856240c`**, na revisão instalada **`8036b9b071229dbf7e85ea748db70abcbeefc379`**. Integração normal por fast-forward desde `55136b44`, sem force e sem alterações concorrentes em `main` na conferência prévia. [Recibo de instalação e delta](treatment-scope-installation-receipt-20261023.json).
O PDF original guardado foi encontrado pelo nome automático na pasta indicada, conferido em texto e nas **dez páginas renderizadas**, sem o regenerar. Conservados nome, bytes e SHA-256 `010eb4ea12015563c1b2f8bc255b1078c276f824c50690732541d5bffe34b8a6`. A revisão 13 contém a segunda oportunidade/Manica, os cinco requisitos específicos e os valores 123456/45678/77 778,00 MZN, com paginação legível. A diferença do nome literal pedido explica o erro anterior de localização; a máquina, conta, sessão e extensão foram confirmadas.
Instalador: **installed**, conclusão `2026-10-23T16:46:13.798467+00:00`, zero migrations/importações, observador activo e fila vazia. Revisão pública confirmou `8036b9b0`, build `16:46:11.203132 UTC`, manifesto SHA-256 `6021421c4ab12146c5fae9c8966e3dae5c8bd1361132e12184e65e00b7e8951e`. Os **15 ficheiros funcionais** afectados coincidem por hash com os blobs Git instalados. Os controlos automáticos do instalador passaram; não se repetiu a ronda manual de provas aceites.
**Delta instalado conferido por leitura autenticada:** ficha real **Florestas — Corredor da Beira**, oportunidade **53**, dossier existente `958df138-7980-4685-8389-fa90be02ed6b`. Novas ligações Simular/Etapas transportam a mesma chave; selector do dossier mostra 53/Sofala; atalhos preservam o âmbito. GET do contexto devolve `consumer_opportunity=opportunity:53`, snapshot apenas dessa oportunidade e dos seus requisitos. A decisão existente «Não precisa de terra» conserva a dispensa da acção no dossier observado. Versão do dossier **5**, igual ao recibo anterior; não foram criados, gravados ou submetidos pedidos no destino, nem aberta a edição do rascunho privado do simulador. As provas positivas de dois pedidos/duas oportunidades feitas em QA são reutilizadas, sem as reproduzir na homologação.
Complementos `d8224ba8`, fechos `6b544548`/`4098192a` e quatro consumidores `55136b44`/`b6a626c9` preservados; nenhuma fixture sintética, alteração de schema/configuração/permissões, Estatísticas ou IA integra este transporte. Mantidas todas as identidades da matriz e as linhas alheias. O recibo posterior fica na branch de trabalho, sem provocar outra instalação apenas documental.
**Saldo corrente:** esta frente está **entregue na homologação**, com PDF e delta instalados conferidos. A frente dos projectos âncora mantém a análise `6985f82e`: filtro sectorial e ideia directa sem oferta por implementar; reconciliação territorial entre ambientes e prova dirigida de interesse/autenticação por concluir. Não é declarado fecho global do percurso nem realizada antecipadamente a avaliação final pela Tempo. As indicações de «QA», «aguarda PDF» ou «não entregue» nas passagens históricas seguintes estão superadas apenas para este recorte.
## PDF nativo guardado — conferência concluída, 23/10/2026
Conferido o ficheiro **existente**, sem regenerar o PDF ou repetir Word/ZIP/provas aceites: `outputs/multi-opportunity-fix-20261023/Relatório — SYNTHETIC QA - escolha comum aos tres consumidores.pdf`. A pasta era a indicada; o nome foi o título automático do navegador, não `candidata-1afdfee8.pdf`. Leitura na máquina **ONALA**, utilizador **macbook**, sessão **Console**; extensão real `.pdf`, 191637 bytes. O erro anterior era «Cannot find path … candidata-1afdfee8.pdf … because it does not exist» para esse nome literal; a listagem da mesma pasta encontrou o original. Não houve renomeação, cópia nem mudança de máquina.
SHA-256 `010eb4ea12015563c1b2f8bc255b1078c276f824c50690732541d5bffe34b8a6`; PDF 1.4, produtor **Skia/PDF m155**, criado pelo Edge, revisão **13**. Texto e **dez páginas renderizadas do ficheiro guardado** conferidos: oportunidade `309`, Manica/parque correspondente; cinco requisitos próprios e fundamentos, estado **Por iniciar**; pressupostos **123456 MZN**, **45678 MZN**, diferença **77 778,00 MZN**; sem Sofala nem `not_started`. Paginação 1/10–10/10, A4, cabeçalhos de tabela repetidos, linhas completas, margens/rodapés legíveis e sem sobreposição ou corte visível. A última página contém as últimas referências e a indicação dos anexos; não está vazia. As quebras de linha do PDF foram normalizadas apenas para procurar texto, sem alterar o documento.
Prova: `native-pdf-inspection.json`, texto e páginas em `outputs/multi-opportunity-fix-20261023/`; [recibo actualizado](native-pdf-and-anchor-review-20261023.json). A condição de conferência do PDF está satisfeita para a candidata `1afdfee8` com a correcção delimitada `2856240c`. Disponibilização Git autorizada pela Tempo em execução; o fecho de entrega só será registado após confirmar a revisão e o delta instalados. As menções abaixo ao ficheiro em falta são históricas e superadas por esta descoberta/conferência. Análise dos projectos âncora conserva o saldo delimitado em `6985f82e`; Estatísticas e IA continuam adiadas.
## Duas frentes — PDF nativo e análise dos projectos âncora, 23/10/2026
Pedido expresso da Tempo após `32144aa2`: conferir o PDF nativo guardado da candidata `1afdfee8`, depois integrar/disponibilizar pelo Git e conferir só o delta; analisar projectos âncora por sector/localização e continuidade para manifestação, dossier e simulador. Word, ZIP e provas aceites não foram repetidos. Estatísticas e IA permanecem adiadas. Este registo sucede ao estado «por analisar» abaixo.
**Candidata:** o botão real de impressão foi aberto no novo dossier QA `d279172b-8bd1-4074-b000-ee25f0d29778`, oportunidade `309`, Manica. A saída revelou duas falhas delimitadas: estado de etapa exposto como `not_started` e ausência do detalhe dos requisitos específicos. Corrigidas na composição do relatório: estado humano existente, requisitos só da oportunidade seleccionada, com documentos/fundamento, e exclusão dos ramos dispensados. Novo teste exclusivo `native-pdf-scope.test.mjs` passou; TypeScript e build passaram. Não se voltou a testar Word/ZIP. A revisão **13**, aberta pelo botão do portal após a correcção, apresenta `Por iniciar`, os cinco requisitos da segunda oportunidade e os pressupostos QA **123456 MZN**, **45678 MZN**, diferença **77 778,00 MZN**. A primeira oportunidade/Sofala não integra esse âmbito. Estes números são uma fixture sintética para conferência da cópia, não nova aceitação dos cálculos.
**PDF guardado ainda pendente:** o controlo do navegador permite abrir e ler a saída, mas não operar o diálogo nativo do sistema. Foi pedida a gravação manual da janela já preparada em `outputs/multi-opportunity-fix-20261023/candidata-1afdfee8.pdf`. Até ler/renderizar o ficheiro e conferir paginação, requisitos, âmbito e valores, a candidata continua exclusivamente QA; não houve push de disponibilização. `origin/main` foi consultado por `git fetch`: continua `55136b44`, sem alterações concorrentes por integrar nessa consulta. Preparar o avanço normal, sem force, e voltar a consultar antes de integrar. Não é necessária nova autorização de disponibilização; a ordem já foi expressa pela Tempo, condicionada à conferência do PDF.
### Projectos âncora — análise concluída e correcções/verificações delimitadas
Reutilizados os registos de reposição (`7c1f5321`, recibo de preparação adaptativa: 29 fichas/15 projectos, IDs/títulos preservados) e de continuidade carteira/oportunidade (`InvestmentPortfolioContinuityTest`, instalado, sem repetir). **Não foi confirmada perda de fichas ou revogação da divulgação.** Revisão estática do código actual e inventário de leitura apenas na base QA isolada, sem escrita. O inventário QA encontrou 15 fichas documentais de projecto com **sector, localização e região em texto** em todas elas, mas sem `primary_territory_id`, ligações públicas em `project_locations` ou oportunidades públicas directamente associadas. Uma décima sexta ficha marcada `SYNTHETIC QA — associação escolhida`, com uma oportunidade, foi identificada e excluída da contagem documental. A diferença QA não é atribuída à homologação; qualquer reposição de dados depende de comparação com o último estado demonstrado e identidades de acesso equivalentes, conforme AGENTS.
| Trecho | Encontrado / classificação | Correcção ou verificação finita |
| --- | --- | --- |
| Pesquisa por sector e localização | **Lacuna de implementação do filtro sectorial.** `InvestmentPortfolioController::index` e `portfolio/index` aceitam pesquisa textual, tipo e hierarquia territorial; não recebem nem apresentam sector. Texto livre contendo um sector não equivale ao filtro combinado. A pesquisa territorial já tem implementação por IDs e relações documentadas em `InvestmentGeography::constrainDossiersToTerritories`. | Acrescentar filtro sectorial e combiná-lo com a localização no mesmo catálogo, conservando filtros ao abrir/regressar. Reutilizar o catálogo de sectores; uma âncora pode abranger vários sectores documentados. Não classificar por semelhança de palavras nem criar ofertas para obter resultados. |
| Correspondência dos dados documentais | **Verificação de dados/ambiente em falta**, distinta da ausência do filtro. Os 15 sectores/localizações existem nos textos QA; o schema de `projects` não tem `sector_id` e os campos/relações territoriais consultados estão vazios nesse ambiente. | Comparar schema, dados, configuração e acesso equivalente com o último estado demonstrado/destino. Reutilizar fonte, exemplar e correspondências já aceites; delimitar as correspondências canónicas em falta e ambiguidades. Não pedir outra validação genérica da fonte, inventar território, nem declarar regressão a partir do clone. |
| Ficha → manifestação | **Mecanismo existente a preservar:** o botão cria rascunho com o mesmo `dossier_id`; `InvestmentSubmissionController::create` conserva `project_ids`. A ficha recupera os rascunhos existentes. **Sem nova prova de execução deste ramo nesta análise.** Para anónimos, o componente apresenta ligação simples a `/login`; a retoma com a identidade/filtros requer prova dirigida. | Conferir apenas login/retoma e recuperação do interesse na mesma ficha, sem submissão externa. Não transformar a ficha documental em oportunidade nem alterar os estados de publicação. |
| Ficha sem oportunidade → preparação → simulador | **Integração directa ainda não implementada.** A ficha mostra oportunidades relacionadas quando existirem; essas usam `from_dossier`, território e `portfolio_origins` já aceites até ao simulador. Sem oferta associada não existe acção de ideia baseada na própria âncora, e `InvestorContext::reference` não suporta uma origem de carteira autónoma. O vínculo explícito manifestação/dossier já existe, mas não cria essa origem no contexto. | Reutilizar o arranque por ideia e o mesmo dossier, acrescentando a referência documental da âncora independente de uma oferta. Conservar identidade, versão, fonte e localização escolhida até ao simulador/relatório e à manifestação vinculada. Conferir este ramo novo e a transição interesse/preparação; reutilizar as provas aceites do ramo com oportunidade. |
O recorte âncora deixou de estar «por analisar»: há **duas lacunas de implementação** (filtro de sector e entrada directa por ideia de âncora sem oferta), **uma reconciliação de dados/ambiente** e **uma prova dirigida de interesse/autenticação**, com a continuidade do ramo novo incluída no seu critério de aceitação. Não se implementou esse recorte nesta análise nem se declarou homologação corrigida. As lacunas de implementação não são regressões demonstradas. Fontes locais: `InvestmentPortfolioController`, `InvestmentGeography`, `InvestmentRelations`, `InvestmentOpportunityChoices::portfolioOrigins`, `InvestorContext`, `InvestmentSubmissionController`, `portfolio/index`, `portfolio/show`, `search-territory`; inventário QA em `outputs/multi-opportunity-fix-20261023/anchor-analysis-data.json`. Actualização nas mesmas linhas `AUD-REL-02`/`AUD-LEG-06` da matriz existente, sem nova matriz/backlog.
## Oportunidade em tratamento — correcção conjunta do saldo em QA, 23/10/2026
Origem: pedido expresso da Tempo após `bc84fbdb`. Corrigir conjuntamente os três consumidores da escolha de oportunidade; conferir os quatro recortes delimitados, corrigindo apenas falhas encontradas. **Só QA; não entregue na homologação nesta passagem.** Permanecem encerrados `6b544548`, `d8224ba8` e `55136b44` com os respectivos recibos; não se repetiram os seus testes. A lista histórica de sete itens abaixo passa a ter os seguintes estados correntes: Correc??o funcional `1afdfee8`; [recibo QA](investment-treatment-scope-qa-receipt-20261023.json).
| Item | Resultado corrente e prova dirigida |
| --- | --- |
| 1 — Simulador | Selector disponível na entrada geral; entrada da ficha/atalho transporta a chave; gravação e recuperação conservam a escolha e o snapshot, e a devolução financeira identifica a oportunidade. A troca aguarda a recuperação do contexto antes de gravar a nova associação. Provas API novas e conferência Edge: segunda/Manica → primeira/Sofala → segunda, no novo dossier QA. Valores deliberados continuam sujeitos à reconciliação existente. |
| 2 — Etapas/requisitos | Decisões, motivos e estados específicos conservados por chave da oportunidade no payload cifrado existente; dados e decisões comuns anteriores preservados. Numa selecção múltipla, uma dispensa comum não dispensa automaticamente todas as fichas. Primeira sem necessidade de terra e segunda com necessidade produzem etapas/acções distintas. Os links dos requisitos conservam a oportunidade; a próxima acção e o snapshot seguem o âmbito escolhido. |
| 3 — Terra/DUAT | Atalho procura o pedido do titular com o mesmo dossier **e a mesma oportunidade**, não o último de outra ficha. Teste cria dois pedidos pela ordem inversa e recupera cada associação. Sem escolha numa selecção múltipla, abre preparação com escolha, sem abrir arbitrariamente um pedido existente. |
| 4 — Retirar a primeira | Prévia, confirmação e recuperação por HTTP dirigidas: segunda ficha exactamente preservada, título comum deliberado conservado e pedido da primeira continua consultável como histórico. Não constitui nova avaliação visual integral do editor. |
| 5 — Dados comuns | Detectada duplicação de contactos já autorizados no payload do dossier; proposta de email/telefone ao simulador e Terra e nome a Terra corrigida. Testes verificam proposta e conservação de contacto deliberado. Perfil/empresa e organização mantêm os mecanismos próprios aceites; não se escolhe empresa/representante automaticamente, nem se converte país de residência/constituição em residência fiscal ou território do projecto. |
| 6 — Entradas alternativas | Teste novo de ideia livre e fontes explícitas sector/território: confirmação e recuperação no mesmo dossier e propostas territoriais em Terra/simulador. Teste de login real do cliente HTTP conserva o URL pretendido e a fonte sectorial. Não se repetiu a prova territorial de reautenticação já aceite, nem carteira/oportunidade. Estas provas HTTP não são apresentadas como nova avaliação visual de todas as entradas. |
| 7 — Exportações | Word e ZIP efectivamente gerados e lidos: identidade da segunda oportunidade e parque correctos, sem Sofala; ZIP contém o Word idêntico. Saída HTML do percurso de impressão contém o mesmo âmbito e distingue etapas aplicáveis/dispensadas. **Permanece por conferir apenas o novo PDF nativo efectivamente guardado pelo navegador**, incluindo paginação/legibilidade; HTML não substitui essa prova. Nenhuma falha do PDF foi presumida. |
Verificações novas: `InvestmentTreatmentScopeTest` (sete casos dirigidos, 170 asser??es; guarda efectiva da base `simulator_fiscal_delta_20261021_test`, porta 55441; transacções), `treatment-scope-export.test.mjs` (dois casos; artefactos reais Word/ZIP e composição de impressão), TypeScript/build. Os erros de preparação dos ensaios (nome de campo de versão no teste e confirmação territorial do clone QA) foram corrigidos, sem alterar a funcionalidade aceite. Fixtures e artefactos permanecem em `outputs/multi-opportunity-fix-20261023/`; novo dossier `d279172b-8bd1-4074-b000-ee25f0d29778`, separado dos dossiers aceites. Sem migrations, fixtures publicadas, alterações em dados activos, permissões, Estatísticas ou IA.
### Projectos âncora por sector/localização — recorte por analisar
**Incluído por pedido expresso da Tempo nesta passagem.** Verificar pesquisa combinada por sector e localização, abertura pela identidade existente, continuidade para manifestação de interesse e preparação do investimento, e recuperação no mesmo dossier com fontes/associações documentadas. A reposição/consulta anteriormente aceite de fichas e relações não demonstra, por si só, este requisito completo. **Estado: por analisar; não se presume implementação nem falha.** Registado em `AUD-REL-02` e articulado com `AUD-LEG-06`, sem nova matriz/backlog. Este recorte permanece no saldo antes da avaliação final pela Tempo; Estatísticas e IA continuam adiadas.
O saldo corrente separa, portanto, a correcção executável QA dos três pontos, as provas dirigidas com os limites acima, a conferência restante do PDF nativo e o novo recorte âncora por analisar. Não há fecho global nem declaração de entrega desta candidata na homologação.
## Saldo finito da aplicação transversal — 23/10/2026
Pedido expresso da Tempo: substituir a qualificação genérica de cobertura parcial por pendências delimitadas, sem repetir provas aceites. Este é o inventário corrente dentro do manual e da matriz existentes; as avaliações anteriores abaixo ficam como histórico, subordinadas aos fechos posteriores. Base revista: `b6a626c9`, com código entregue em `55136b44`. Revisão de código, contratos, testes existentes e recibos; **não houve execução funcional nova**, alteração de dados ou instalação nesta passagem. Uma lacuna de código abaixo não é apresentada como novo incidente observado no navegador.
### Corrigido, demonstrado e entregue — manter encerrado
| Recorte | Entrega / prova reutilizada |
| --- | --- |
| Gravação/idioma, origem dos dados no relatório, sete ligações da Beira | `6b544548`; recibo `4098192a`. |
| Complementos com identidade, necessidade sem oferta, serviço e relação à oportunidade no dossier | `d8224ba8`; recibo `af926180`; serviço sintético exclusivamente QA, Fiação em preparação/localização por definir. |
| Zonas/parques, Terra/DUAT, Balcão e manifestação: tratar a oportunidade escolhida, alterar só a segunda e conservar a primeira, guardar/recuperar associação e relatórios cobertos | `55136b44`; recibo `b6a626c9`; QA `4a01e9e2`. Inclui escolha explícita quando há várias, edições deliberadas e associação bloqueada de pedido devolvido. |
| Resumos editáveis no simulador, nomes territoriais, terreno Sim/Não/Por definir, ocultação/reabertura de etapa global e continuidade carteira/dossier | Incrementos `7ca94967`, `f2150b19`, `5bda74ae`, entregues na linha `7c1f5321` e preservados pelas entregas seguintes; recibos de preparação adaptativa e continuidade existentes. Não voltar a exigir a instalação indicada como pendente em notas históricas. |
### Pendências actuais — sete recortes, sem expansão implícita
| N.º / situação | Ecrã ou consumidor e limite exacto | Resultado necessário / próxima acção delimitada |
| --- | --- | --- |
| 1 — Falha de integração identificada no código | **Simulador iniciado pelo dossier com várias oportunidades.** `InvestorContext::prefill()` só delimita o simulador quando recebe a chave; `DossierPrefill` exclui o simulador do selector de oportunidade. A entrada geral continua a propor os campos comuns derivados da primeira, embora mostre todas as fichas. | Permitir identificar explicitamente o âmbito da análise e recuperar a oportunidade em tratamento; tornar explícito o âmbito comum quando for essa a escolha. Manter rascunhos, edições e relatórios aceites. Não somar investimentos nem inventar uma regra de agregação financeira. Prova nova apenas: seleccionar a segunda neste consumidor e conferir a sua localização/sector/parque e a identificação do âmbito no resultado. |
| 2 — Falha de integração identificada no código | **Dossier, etapas e próxima acção.** `InvestorPath::view()` calcula aplicabilidade por `investor_path.facts` global; `land_needed=no` oculta a acção Terra para o dossier todo. `InvestorContext::landPreparation()` também lê esse único facto. Não há distinção de decisões de terra diferentes entre duas oportunidades. | Conservar a resposta global válida, mas impedir que dispense silenciosamente a necessidade específica de outra oportunidade. Ligar decisão, explicação e próxima acção ao âmbito tratado; desconhecido continua pendente. Prova nova: uma oportunidade sem necessidade e outra com necessidade, sem repetir o ciclo global Sim/Não já aceite. |
| 3 — Falha de integração identificada no código | **Atalho Terra/DUAT no resumo do dossier.** `InvestorPath::view()` procura o caso mais recentemente actualizado do titular com o mesmo dossier, sem filtrar `context_opportunity`; o atalho abre esse caso. A correcção dos formulários não torna esta escolha do resumo específica. | Com dois pedidos de oportunidades diferentes, apresentar escolha identificável ou atalho por oportunidade, recuperando o pedido correspondente. Preservar os pedidos e respectivos snapshots; não substituir pelo último pedido do dossier. A lista de pedidos/resultados deve permitir distinguir as associações. |
| 4 — Ainda sem prova delimitada | **Editor de contexto: retirar, substituir ou reordenar a primeira oportunidade.** A prova aceite altera apenas a segunda; o código ainda projecta a primeira em `selection`/campos comuns. Testes de mudança territorial e de conservação de várias fichas são reutilizados, mas não demonstram este impacto na interface. | Conferir apenas a prévia da retirada/substituição da primeira, confirmação e recuperação: campos comuns propostos, edições preservadas, segunda intacta e pedidos antigos mantidos como históricos. Não declarar perda nem pedir nova prova da alteração exclusiva da segunda. |
| 5 — Ainda sem prova delimitada | **Reutilização perfil/empresa/organização → contexto e ferramentas.** A manifestação já recebe conta e empresa em `InvestmentSubmissionController::create()`; o perfil já existe. Isso não comprova a herança dos dados equivalentes na preparação do dossier, Terra e simulador. | Traçar apenas campos equivalentes e autorizados (identificação/contacto, representação, país/origem quando semanticamente coincidentes), registar o que chega, o que volta a ser pedido e a prioridade de edições locais. Corrigir só duplicação demonstrada. País de constituição, residência fiscal e localização do projecto não são intercambiáveis. Não repetir o preenchimento da manifestação já coberto. |
| 6 — Ainda sem prova delimitada | **Entradas por ideia, sector ou localização com passagem para operação privada.** Há prova de rascunho/reauth territorial em `InvestmentCorridorContextTest` e de carteira/oportunidade nos recibos aceites. Não é prova de todas estas entradas alternativas até ao consumidor. | Conferir somente cada entrada alternativa ainda não coberta: fonte e escolhas no arranque, regresso/autenticação e recuperação no mesmo dossier; pedir apenas dados ausentes. Reutilizar o caso territorial e as entradas carteira/oportunidade existentes, sem os repetir. Não pressupor que falham. |
| 7 — Ainda sem prova delimitada | **Exportações nativas do dossier/plano para âmbito múltiplo.** Valores, idioma, proveniência, exportações financeiras e recuperação já têm provas próprias; relatório Terra e associação canónica dos consumidores também. Falta isolar o efeito da escolha de âmbito nos restantes artefactos nativos, após resolver 1–3. | Conferir apenas correspondência oportunidade/localização/parque, etapas aplicáveis e identidade de pedidos em Word/PDF/ZIP efectivamente gerados. Não repetir cálculos, idioma, sete ligações, anexos nem o relatório Terra aceites. Um formato que não inclua esse conteúdo deve ser registado como sem aplicação, não como falha. |
São **três lacunas de integração constatadas por leitura e quatro verificações delimitadas**, não sete falhas de execução. Os itens 1–3 determinam a correcção executável seguinte; 4–6 podem ser conferidos independentemente em QA isolado; 7 depende apenas do âmbito corrigido. A contagem refere-se a estes sete recortes, não a cada formato ou campo. Uma verificação que passe encerra o item; uma que falhe regista o defeito concreto na mesma linha da matriz. Não criar outra matriz/backlog nem converter ausência de prova em regressão.
### Nunca implementado / desenvolvimento futuro — separado do saldo de regressões
O editor unificado de actores, fornecedores, contactos e infraestruturas das fichas territoriais e a cartografia de linhas de corredores não existem no produto demonstrado. Candidaturas com procedimento autónomo, restauro automático de snapshots e entrega a CRM remoto sem conector configurado também não são funcionalidades perdidas. Permanecem nos respectivos requisitos de desenvolvimento, fora dos sete recortes acima; não são pré-condições para os corrigir. Ausência de oferta ou associação documental não autoriza criar uma relação, localização ou publicação. Estatísticas e IA continuam adiadas por decisão da Tempo.
### Rastreabilidade e limite desta revisão
Consumidores revistos: editor/arranques de contexto, resumo e etapas do dossier, simulador e reutilização, parques, Terra/DUAT, Balcão, manifestação, perfil/empresas e composição dos relatórios. Fontes: `InvestorContext`, `InvestorPath`, `InvestmentConsumerScope`, `InvestmentSubmissionController`, `dossier-prefill`, `investment-opportunity-choices`, `project-dossier`, testes `InvestmentCorridorContextTest`, `InvestmentConsumerScopeTest`, `InvestorPathTest` e `context-inheritance`, recibos de instalação existentes. Não se auditou novamente gestão editorial, publicação, RLS, legislação ou cálculos fiscais; mantêm os seus inventários e aceitações próprios. A avaliação final de navegação e apresentação pela Tempo vem depois do fecho desta lista. Referências na matriz existente: `AUD-LEG-06`, `AUD-SIM-INT03`, `AUD-SIM-INT06`.
## Oportunidade em tratamento — entregue na homologação, 23/10/2026
Por instrução expressa da Tempo, a correcção QA `4a01e9e2` e o recibo `55136b44` foram integrados no `main` existente por avanço directo, sem force ou conflitos. O destino serve **`55136b44`**, confirmado em `https://teste-investidor.tempo.co.mz/build/revision.json`, build `2026-10-23T15:55:10.921100+00:00`, manifest SHA-256 `b2dd6172516f220344ede5fc9d7876b7fc8417416d03842d737452709379a12c`. [Recibo da instalação](multi-opportunity-context-installation-receipt-20261023.json).
Conferido apenas o delta instalado, com sessão normal já autenticada: a ficha real **Florestas — Corredor da Beira**, referência `INV-MZ-GEN-8326396601945337`, ID **53**, apresenta as ligações para Terra/DUAT e Balcão com `opportunity:53` e o dossier existente `958df138-7980-4685-8389-fa90be02ed6b`. GET autorizado do contexto com essa identidade devolveu apenas essa ficha no snapshot específico e Sofala nas propostas de Terra/DUAT, Balcão e manifestação. Não se criou, guardou ou submeteu qualquer pedido para esta conferência. Provas locais: `outputs/opportunity-consumer-install-20261023/`, com revisão pública, ligações, resumo da resposta e imagem; hashes no recibo.
As provas QA aceites dos quatro consumidores foram reutilizadas, sem repetir o ensaio da segunda oportunidade. O estado «concluído em QA, ainda não instalado» dos registos históricos abaixo é superado neste recorte pela revisão servida. O histórico de `d8224ba8` e das alterações concorrentes permanece ancestral da entrega; os componentes nucleares dos complementos e o recibo dos fechos anteriores coincidem por blob Git. Sem delta de migrations, configuração ou dependências, nem transporte de dados/fixtures; as oportunidades e serviços reais mantêm os estados de publicação existentes. A disponibilização usou o instalador automático existente, sem edição manual da pasta servida.
**Saldo:** zonas/parques, Terra/DUAT, Balcão e manifestação deste recorte estão agora **entregues na homologação**, com o delta instalado conferido. Complementos `d8224ba8` e fechos `6b544548`/`4098192a` preservados. Estatísticas e IA permanecem adiadas; não há alterações nesses módulos/configuração. A aplicação transversal fora deste recorte permanece parcial, sem declarar aceitação global nova. Actualizadas apenas as evidências de AUD-LEG-06 e AUD-SIM-INT03 na matriz existente.
## Oportunidade em tratamento — correcção executável QA dos quatro trechos, 23/10/2026
Pedido expresso da Tempo: corrigir as falhas confirmadas em `ad4160f0`, sobre a versão actual e preservando os complementos entregues em `d8224ba8`. Implementação `4a01e9e2` na branch `codex/opportunity-consumer-scope-20261023`. [Recibo QA](multi-opportunity-context-qa-receipt-20261023.json). Esta passagem supera o estado «não corrigido» do diagnóstico apenas nos quatro trechos aqui demonstrados; o diagnóstico histórico permanece preservado.
Cada oportunidade guarda agora a sua própria referência de zona/parque. A consulta e a compatibilidade usam o corredor e o território dessa oportunidade; alterar a segunda actualiza apenas as suas opções. O parque comum de registos anteriores é recuperado na primeira identidade a que pertencia. O rascunho permite conservar uma localização ainda por escolher após a mudança de corredor, sem repor silenciosamente a localização documental incompatível.
Terra/DUAT, Balcão e manifestação resolvem a oportunidade em tratamento pela identidade dentro do dossier autorizado. A entrada identificada na ficha/ligação transporta essa identidade; a manifestação mantém a identidade da ficha de origem ao associar o dossier. Sem escolha e com várias oportunidades, a interface pede uma selecção clara e propõe dados comuns, sem adoptar o território da primeira. Os links por oportunidade no dossier e na ficha dão entrada em Terra/DUAT e no Balcão. A reconciliação existente conserva edições deliberadas e detalhes locais das parcelas; os limites dos pedidos devolvidos no Balcão mantêm-se. O resumo geral de um dossier múltiplo deixa de apresentar a primeira como se fosse necessariamente a oportunidade em tratamento.
O servidor guarda no payload cifrado existente de cada pedido uma associação canónica: oportunidade, corredor, localização, parque, origem/versão e requisitos dessa oportunidade. Um snapshot enviado pelo cliente não substitui a origem autorizada. A manifestação conserva também a coluna de oportunidade coerente com a escolha e a associação nas versões/intake. O relatório imprimível de Terra/DUAT apresenta a associação guardada; o recibo do Balcão apresenta o contexto congelado; o modelo comum do relatório conserva cada parque junto da respectiva identidade.
| Prova dirigida | Resultado |
| --- | --- |
| Alterar apenas a segunda | Novo dossier sintético QA `1017980b-a61c-44a4-b8d5-bd909c2d1316`: segunda `309`, Gaza/Limpopo/parque QA Gaza → Manica/Beira/parque QA Manica pela interface, com motivo, confirmação, gravação e recuperação. Primeira `286` continua em Sofala/parque QA Sofala, exactamente igual à sua estrutura inicial por comparação integral. |
| Terra/DUAT | Entrada pela ligação da segunda, gravação e recuperação do pedido `01a16967-f058-7353-a7bc-33839b3c6595`. Identidade 309, Manica, parque Manica e requisitos próprios recuperados. Relatório HTML imprimível aberto e conferido visualmente com a mesma associação. |
| Balcão | Entrada com a segunda identificada; pedido `c3de8e43-701b-46bd-a016-64adc32e2394` preparado, guardado e recarregado. Mesma identidade, Manica, parque e requisitos. O template de recibo foi verificado em teste transaccional, sem inventar uma submissão real. |
| Manifestação | Preparada a partir da ficha existente da segunda; dossier associado pela interface. Pedido `01a16968-e3d2-728a-b28a-2fb05540509e` guardado e recarregado com 309/Manica/parque/requisitos. A versão guardada conserva o vínculo. Não houve submissão externa. |
| Várias sem escolha | Entrada Terra/DUAT apenas com o dossier apresenta a escolha de oportunidade, sem localização da primeira. Selecção explícita da segunda recupera Manica, o seu parque e os seus requisitos. |
Ambiente: Edge, `127.0.0.1:18574`, actor sintético normal QA 509 e guarda efectiva de `simulator_fiscal_delta_20261021_test:55441`. Reutilizadas as oportunidades QA existentes; preparados **três parques expressamente SYNTHETIC QA**, só na base isolada, para conferir opções positivas e incompatibilidade. Nenhuma oferta, oportunidade, serviço ou publicação real foi criado por essa preparação. O dossier aceite dos complementos conserva as três oportunidades/localizações e quatro relações, sem repetir os seus casos funcionais.
Validação final: **4 testes de integração, 104 asserções; 3 testes frontend dirigidos; TypeScript e build aprovados**. Cobertos também identidade alheia/actor alheio, snapshot forjado, conservação de edições deliberadas, parcela, rascunho territorial incompleto e associação bloqueada num pedido devolvido. Provas: `outputs/multi-opportunity-fix-20261023/verified-state.json`, textos recuperados e `land-report.png`; hashes no recibo. O ensaio do relatório é HTML imprimível, sem alegar nova aceitação de PDF nativo.
**Disponibilidade:** correcção executável e demonstrada em QA; `main` e homologação continuam em `d8224ba8`. Sem migration, importação ou escrita activa. Não se transportam as fixtures. Complementos e fechos `6b544548`/`4098192a` preservados. A aplicação transversal fora deste recorte continua parcial; não se declara fecho global nem aceitação organizacional nova. Actualizadas apenas as evidências de AUD-LEG-06 e AUD-SIM-INT03 na matriz existente.
## Contexto específico por oportunidade — conferência delimitada, 23/10/2026
Pedido da Tempo: complementos entregues em `d8224ba8`, sem pendência de disponibilização nem repetição dos seus casos ou dos fechos `6b544548`/`4098192a`. Conferida a alteração apenas da segunda oportunidade nos percursos ainda parciais. [Recibo técnico](multi-opportunity-context-review-20261023.json).
Reutilizado o dossier sintético QA existente `6fdbb963-726e-4048-9884-8cddc04e77a6`, actor normal QA 509, base guardada `simulator_fiscal_delta_20261021_test:55441`. Prévia, confirmação e recuperação pelos serviços existentes, dentro de transacção revertida: segunda oportunidade `309`, Gaza/Limpopo → Manica/Beira, com confirmação e motivo explícitos. Primeira e terceira escolhas conservadas exactamente. A versão interna passou 2 → 3; após rollback o registo original de `desk_cases` coincidiu integralmente com o anterior. Não se alterou a homologação nem se reavaliaram complementos.
| Percurso | Resultado e causa delimitada |
| --- | --- |
| Dossier | A alteração da segunda foi recuperada separadamente; a primeira continuou Sofala e a terceira Gaza. Não há perda geral das localizações neste ensaio. |
| Zonas/parques | Consulta e dependências do efeito de `context.tsx` usam apenas `choices[0]`; existe só `selection.park`, validado pelo servidor contra a primeira escolha. Alterar a segunda não muda os parâmetros dessa consulta nem permite atribuir-lhe parque próprio. Os conjuntos QA consultados estavam vazios: não se declara disponibilidade positiva nem eliminação de um parque. |
| Terra/DUAT | Antes/depois, a proposta continua uma parcela de Sofala, sem identidade da oportunidade. `prefill()` deriva-a dos campos comuns; o formulário aplica só `v[0]` à primeira parcela. Conferência visual de leitura mostrou as referências comuns e os complementos existentes, sem lista das localizações de cada oportunidade. |
| Balcão | Território e descrição continuam os da primeira escolha. A diferença executada é o texto da revisão v2 → v3 na origem territorial; isso não transporta o âmbito da segunda oportunidade. |
| Manifestação | Sector, objectivo e localização propostos permanecem iguais, incluindo Sofala. A proposta não identifica a oportunidade a que cada âmbito se destina. |
**Saldo:** causas confirmadas; estes quatro trechos não ficam fechados nem corrigidos por esta avaliação. O componente comum só apresenta `OpportunityChoices` no simulador. A correcção deve identificar expressamente a oportunidade destinatária das zonas/parques e dos dados propostos em cada formulário, preservando as restantes escolhas e edições deliberadas, e demonstrar guardar/recuperar após mudar apenas a segunda. A confirmação/recuperação deste ensaio foi nos serviços da aplicação, não nova sessão de navegador nem submissão dos três formulários. Evidência local: `outputs/multi-opportunity-review-20261023/probe.json` e `land-visible.txt`. Sem nova matriz/backlog; actualizada apenas a evidência de AUD-LEG-06 e AUD-SIM-INT03 na matriz existente. A aplicação transversal permanece parcial.
## Complementos — disponibilização em homologação, 23/10/2026
Por decisão expressa da Tempo, o recorte demonstrado foi integrado pelo Git existente e disponibilizado em https://teste-investidor.tempo.co.mz, revisão `d8224ba8`. A integração `088f2c2c` preservou as alterações concorrentes de `43cd0be7`; `d8224ba8` actualizou apenas o teste da entrada pública do simulador à estrutura da página inicial integrada. O instalador tinha recusado a referência ao componente antigo, também falhada no main concorrente, conservando a release anterior. Não se desactivou a verificação.
O recibo existente regista a revisão efectivamente servida, a instalação e a conferência delimitada do delta. Mantêm-se as provas QA aceites, sem nova ronda dos mesmos casos. Não houve migrations, importações nem transporte dos exemplos sintéticos. As oportunidades e serviços reais conservam identidades e estados de publicação. Fiação permanece necessidade em preparação, sem localização ou oferta inventadas.
Os estados «homologação inalterada» e «não instalado» nas passagens históricas abaixo descrevem essas passagens e são superados apenas neste recorte. As pendências de contextos específicos em zonas/parques, Terra/DUAT, Balcão e manifestação continuam abertas; a aplicação transversal permanece parcial. Fechos `6b544548`/`4098192a` preservados.
## Complementos — conferência visual QA concluída no recorte pedido, 23/10/2026
Pedido da Tempo: conferir abertura da ficha complementar existente, preparação de necessidade sem oferta identificada, selecção de serviço/instrumento e recuperação de selecção/origem/localização; com várias oportunidades, verificar a ligação à oportunidade escolhida, sem assumir a primeira. Homologação inalterada e fechos anteriores preservados. Esta passagem supera **apenas a pendência visual dos complementos neste recorte** nas secções abaixo; não declara a aplicação transversal concluída.
Ambiente usado: navegador Edge em `http://127.0.0.1:18574`, servido pelo router QA existente, com guarda efectiva de `simulator_fiscal_delta_20261021_test:55441` em cada pedido. Reutilizada a sessão normal da conta sintética QA 509; sem criar sessão, contornar MFA ou conceder papéis. Criado pela interface o dossier **SYNTHETIC QA — conferência visual de complementos 23-10-2026**, ID `6fdbb963-726e-4048-9884-8cddc04e77a6`; nenhum dossier anterior foi editado.
| Passagem visual | Resultado demonstrado |
| --- | --- |
| Abrir oportunidade complementar existente | Silos/armazenagem → Milho, trigo e arroz — Corredor da Beira, ID **286**, referência e slug `INV-MZ-GEN-8128691124566423`/`inv-mz-gen-8128691124566423`, conservados. A entrada de preparação recebeu a relação à carteira e a mesma identidade, sem reimportação. |
| Preparar necessidade sem oferta identificada | Acção de Fiação abriu a preparação no mesmo dossier e apresentou **Necessidade complementar — ideia em preparação**. Confirmada e recuperada com origem no Corredor da Beira e Centro. A localização inicialmente não escolhida ficou **Por definir**, sem receber Sofala por associação à primeira oportunidade. |
| Seleccionar serviço associado | Ficha do serviço QA **17**, de Formação, aberta e seleccionada por **Seleccionar este complemento e continuar no dossier de origem**. Origem: segunda oportunidade QA **309**; localização: **Gaza**. Recuperadas após confirmação e recarregamento. |
| Várias oportunidades, sem assumir a primeira | Primeira ficha **286 → Sofala**; segunda **309 → Gaza**. O complemento de frio **310** e Formação **17** ficaram ligados à **309**, em **Gaza**; a primeira ficha continuou em Sofala. A consulta das fichas e o retorno conservaram as escolhas pendentes, incluindo Fiação. |
| Recuperar no dossier | Após **Rever dados antes de guardar → Guardar dados do investimento → Voltar ao dossier → recarregar**, recuperaram-se três fichas e quatro relações complementares: Silos/armazenagem, Fiação, Formação e Cadeia de frio, cada uma com a sua origem e localização informada ou indeterminada. O título deliberado do dossier permaneceu intacto. |
**Âmbito dos exemplos:** a ficha 286 e a origem da carteira são identidades já existentes em QA. A base QA não tinha ofertas de serviços; para a prova visual positiva foram preparados **dois projectos, duas oportunidades (309/310), uma relação complementar pública e um serviço (17)**, todos explicitamente marcados **SYNTHETIC QA**. O serviço foi tornado visível unicamente como fixture da base QA guardada, não como publicação real nem aprovação da Tempo; não foi transportado para a homologação. A prova não declara um serviço real disponível no destino. Os registos anteriores de oportunidades (45), projectos (17), fichas da carteira (30) e parcerias (zero) permaneceram iguais por hash na preparação das fixtures. Originais, relações, permissões e aceitações anteriores foram preservados.
Provas locais: [ficha existente](../../../outputs/complements-visual-20261023/existing-opportunity.png), [serviço e sua origem](../../../outputs/complements-visual-20261023/associated-service.png), [oportunidades recuperadas](../../../outputs/complements-visual-20261023/recovered-opportunities.png) e [complementos recuperados](../../../outputs/complements-visual-20261023/recovered-complements.png). Texto visível recuperado e recibo das fixtures no mesmo directório. Actualizado o [recibo existente](investment-complements-qa-receipt-20261023.json) e a cobertura das cinco linhas existentes da matriz. Não houve nova alteração funcional, migration, instalação, importação ou escrita na homologação nesta passagem; as suites automáticas anteriores não foram repetidas.
**Saldo preservado:** conferência visual positiva dos complementos descritos, incluindo origem na segunda oportunidade. Zonas/parques, Terra/DUAT, Balcão e manifestação com contextos específicos de várias oportunidades **continuam por demonstrar**. Não se declara aceitação da Tempo, disponibilização no destino ou fecho global. Os três fechos de `6b544548`/`4098192a` mantêm-se.
## Complementos ligados ao investimento e ao dossier — preparação QA, 23/10/2026
**Origem funcional:** pedido expresso da Tempo nesta retoma: ligar regadio, silos, frio, processamento, fiação e logística às oportunidades existentes; distinguir necessidades ainda sem oferta; ligar certificação, formação e financiamento aos serviços/instrumentos existentes, conservando contexto e identidade no dossier. Este incremento não reabre gravação/idioma, proveniência do relatório ou Beira → sete oportunidades, encerrados no âmbito de `6b544548`/`4098192a`. Mantêm-se as aceitações e os limites transversais anteriores.
**Causa delimitada no código anterior:** `content.focus` era uma string, validada com os restantes campos textuais em `InvestmentPortfolioController::save` e apresentada por `portfolio/show.tsx` como texto em `<dd>`. Não existia resolução desses complementos para identidades de oportunidades/serviços nem selecção complementar no contexto privado. O bloco separado de oportunidades relacionadas não conservava uma relação tipificada «origem → complemento escolhido». Isso demonstra a integração ausente nesse trecho, não eliminação de fichas ou perda de dados. A tabela `complementary_project_links` já existia; não se criou outra tabela ou cadastro paralelo.
**Comportamento preparado no percurso existente:**
- As fichas públicas da carteira e das oportunidades apresentam os complementos mencionados no seu contexto documental. O resolvedor `InvestmentComplements` cruza a menção com as relações existentes à carteira, as ligações públicas entre projectos complementares ou a cadeia de valor publicamente documentada. A menção isolada não associa oportunidades fora dessas relações. Mantêm-se os estados publicados da classificação, incluindo referências preliminares, sem os promover a confirmação.
- Uma oportunidade identificada abre pela mesma rota/ID/slug; a escolha inclui origem, tipo de complemento, ficha de destino, localização escolhida, versão, trecho, fonte e hash do contexto consultado. Quando a componente está expressamente incluída na própria oportunidade, liga a essa identidade e explica-o, sem criar outra ficha.
- Certificação/formação/financiamento consultam ofertas publicadas da Bolsa explicitamente ligadas à origem, distinguindo `offering` de `seeking`. Serviços ligados à ficha da carteira ou ao seu projecto/cluster também são reutilizados. Financiamento reutiliza o tipo financeiro existente; uma necessidade de financiamento no resumo de um projecto não é, por si, uma oferta. Não se activaram contratos modulares nem instrumentos por importação.
- Sem oferta identificada nas relações desse contexto, apresenta-se **necessidade complementar**, com acção para iniciar uma ideia ou prepará-la num dossier existente. Não se cria nem publica uma oportunidade como efeito dessa acção. A ausência de correspondência neste recorte não declara inexistência absoluta no acervo.
- No editor do dossier, os complementos são consultados por oportunidade e localização; um território conhecido incompatível exclui o destino, e localização desconhecida continua «por definir». As escolhas têm prévia e confirmação, rascunho, retirada explícita e recuperação. Ao consultar/seleccionar outro complemento, a navegação aguarda a gravação das edições pendentes; o contexto acompanha a consulta da ficha pública. Uma entrada antiga retirada não volta por actualização; uma nova selecção explícita pode recuperá-la.
- A relação fica no payload cifrado e no histórico existente do dossier, aparece no dossier e no componente comum dos quatro formulários, e acompanha o snapshot do simulador e as linhas do modelo comum de relatório/exportação. Os dados deliberados do investimento principal conservam-se. Uma oferta retirada não pode entrar como nova escolha; uma relação anteriormente confirmada conserva o snapshot para revisão, sem republicar a oferta. Uma origem indisponível na consulta em lote é assinalada e não oculta os restantes resultados.
**Escolhas técnicas, sem novas exigências organizacionais:** vocabulário literal dos nove âmbitos solicitados para identificar trechos; IDs e relações existentes para resolver destinos; filtragem pela hierarquia territorial pública; selecção complementar no JSON cifrado e confirmação/versionamento já existentes. Não houve migration, novo papel, nova aprovação, importação, publicação, alteração de dados activos ou instalação na homologação. Não se transportaram dados QA para o destino.
**Verificação delimitada:** testes novos de `InvestmentComplementsTest` na base isolada `simulator_fiscal_delta_20261021_test`, porta 55441, com guarda do alvo e transacções revertidas; testes frontend novos em `investment-complements.test.mjs`; TypeScript e build. A prova da cadeia de valor usa um contrato público sintético identificado como tal, sem declarar novas relações reais no cadastro. Recibo resumido: [investment-complements-qa-receipt-20261023.json](investment-complements-qa-receipt-20261023.json). Não se repetiram os três casos aceites. Não se declara conferência visual nova no navegador, instalação ou aceitação da Tempo.
**Guia de utilização preparado:** abrir a ficha principal → **Investimentos e serviços complementares** → consultar a ficha correspondente ou **Iniciar preparação de uma ideia** → seleccionar o dossier autorizado ou criar a ideia → rever os complementos, localizações e diferenças → confirmar no editor. Para retirar, usar **Retirar esta relação da preparação** e confirmar a comparação. Os formulários ligados mostram a relação consultada, mantendo os seus próprios dados e operações de gravação/submissão.
**Saldo:** este incremento cobre a ligação e conservação dos complementos resolvidos pelas fontes/relações acima. Não declara associação automática de todos os complementos a todas as fichas, cadastro completo dos serviços ou cobertura de todas as expressões possíveis. As opções comuns de zonas/parques e a passagem de requisitos específicos de várias oportunidades aos restantes formulários, identificadas na avaliação anterior, continuam parciais. Matriz existente: RF-CLU-003, RF-COR-004, AUD-REL-02, AUD-LEG-06 e AUD-SIM-INT06, sem nova matriz/backlog.
## Avaliação dos trechos restantes — 23/10/2026
Decisão expressa da Tempo nesta retoma: **encerradas no âmbito reportado** gravação/idioma, origem dos dados no relatório e ligação da Beira às sete oportunidades; entrega `6b544548`, recibo `4098192a`. Reutilizar essas provas, sem repetir os casos aceites. **A aplicação transversal permanece parcial.** Esta passagem é avaliação estática do código e inventário de cobertura existente; não é nova prova visual, execução dos testes citados ou alteração na homologação. Não foi confirmada perda adicional de dados ou funcionalidades.
| Prioridade | Implementação e provas existentes a preservar | Limite encontrado / trecho ainda a demonstrar |
| --- | --- | --- |
| Várias oportunidades | `InvestmentOpportunityChoices`, `InvestorContext` e `investment-opportunity-choices.tsx` conservam território, actividades e origens por ficha. O teste existente `test_multiple_opportunities_keep_sites_sources_requirements_and_results_in_one_dossier` cobre confirmação, recuperação, requisitos agregados e resultados preservados. Não foi repetido. | `InvestorContext.php:384–386` escolhe a primeira oportunidade para os campos comuns. Avaliar retirada/mudança da primeira e alteração apenas da segunda, sem atribuir os dados comuns a todas as fichas. A existência do teste não demonstra a interface deste delta. |
| Corredor/localização | Cada cartão tem corredor e caminho territorial próprios; a mudança conserva caminho compatível e retira níveis incompatíveis com explicação. O teste existente `test_corridor_change_keeps_compatible_paths_and_removes_only_incompatible_geography` preserva esta cobertura. | `context.tsx` carrega parques por `choices[0]`, e `InvestorContext` também usa a primeira escolha na compatibilidade comum. Uma mudança apenas na segunda não altera esses parâmetros. Falta demonstrar como se apresenta e distingue a aplicabilidade das zonas/parques nas várias oportunidades. Não inferir que todas perderam a localização. |
| Reutilização nos restantes formulários | `DossierPrefill` está integrado em simulador, Terra/DUAT, balcão e manifestação. `inheritCompatibleFields` preenche vazios, acompanha herança identificada e conserva valores deliberados; a interface permite comparar/substituir campos. Balcão respeita os campos editáveis de um pedido devolvido. | `prefill()` entrega uma parcela territorial comum a Terra/DUAT e valores comuns aos outros destinos. No componente de reutilização, a lista `OpportunityChoices` só aparece para `simulator`. Não está demonstrada a passagem dos contextos específicos de duas oportunidades aos restantes formulários; a ligação ao mesmo dossier não comprova essa cobertura. |
Próxima prova delimitada em QA isolado: duas oportunidades com corredores/localizações distintos; mudar apenas a segunda e conferir opções/requisitos e conservação da primeira; retirar a primeira e comparar o impacto proposto nos campos comuns; abrir Terra/DUAT, balcão e manifestação a partir desse dossier, conferir dados comuns e referências específicas, preservar uma edição deliberada e recuperar os formulários. Usar apenas as etapas aplicáveis, sem manifestação/submissão real. Não repetir idioma/gravação, proveniência do relatório ou as sete ligações aceites. A comparação entre primeira e segunda escolha é um método técnico de avaliação, não uma nova regra de negócio nem decisão de escolher uma localização comum pela Tempo.
Cobertura registada nas linhas existentes AUD-SIM-INT03 e AUD-LEG-06 da matriz; sem nova matriz ou backlog. Nenhuma alteração de código funcional, schema, dados, configuração ou permissões nesta avaliação. Os limites acima permanecem pendentes de demonstração/correcção delimitada; não são declarados resolvidos por documentação.
## Instalação e conferência das três correcções — 23/10/2026
**Revisão servida: `6b544548`.** Conferência visual QA da Beira concluída antes da integração: sete oportunidades visíveis, Florestas abre com origem da carteira conservada nos dois botões de preparação. Integrada por avanço directo no Git, sem alterações concorrentes em main. Executor concluído às 13:45:19 UTC, 206/206 testes frontend; zero migrations/importações e nenhuma alteração de permissões. Recibo: `continuity-corrections-installation-receipt-20261023.json`. As referências abaixo a QA pendente/homologação em `7c1f5321` descrevem o estado histórico anterior, sucedido por esta instalação.
Delta no destino, apenas no dossier privado já identificado como teste `958df138-7980-4685-8389-fa90be02ed6b`: seleccionar Português (Moçambique), Guardar e continuar mais tarde e recarregar recuperou o idioma, com confirmação e sem falso conflito. Relatório imprimível revisão 4 identifica título herdado dos dados introduzidos no dossier, resumo herdado da oportunidade Florestas v2, sector herdado do catálogo sectorial e Sofala do catálogo territorial; sem repetir o teste de todas as exportações/paginação. A guarda do servidor coincide por SHA-256 com a testada em QA; reutilizada a recusa de versões antigas, sem provocar escrita desactualizada em dados do destino.
Na ficha pública da Beira, as sete ligações foram abertas individualmente: Açúcar, Carne bovina, Florestas, Hortícolas, Milho/trigo/arroz, Sésamo e Soja. Títulos coincidentes e origem `from_dossier` conservada em todos os botões de preparação. Não foram publicadas novamente as 34 oportunidades nem alteradas relações por província. Provas e revisão anterior preservadas no recibo. Aplicação transversal continua parcial; este fecho respeita apenas os três deltas pedidos.
## Correcções da verificação de homologação — candidata QA, 23/10/2026
Referência: `c3f7c3ad`; pedido expresso da Tempo nas três prioridades. As 34 oportunidades já públicas e as provas aceites mantêm-se. Esta candidata não altera a homologação, que permanece em `7c1f5321`; sem migrations, novos papéis, publicação, importação ou alteração de dados activos.
1. **Gravação/idioma — causa de transporte reproduzida.** O Nginx do destino comprime JSON e enfraquece o ETag da representação; confirmado em leitura pública do questionário (o controlador produz uma versão forte e a resposta chega `W/"questionnaire-…"`, com gzip). O cliente do rascunho devolvia literalmente o cabeçalho ao servidor, que compara a versão forte exacta; os logs do dossier de teste mostram PUT 200 seguido de 412 e repetição após recuperação. Em QA isolado, um router de prova aplica gzip e enfraquece os ETags dos rascunhos para reproduzir esse transporte. A correcção normaliza apenas o formato conhecido `W/"sfd-UUID-vN"` antes de o usar; não aceita outra versão, não muda o controlo do servidor nem força retry/overwrite. No navegador QA, mudar o idioma para English, guardar e recarregar recuperou English com confirmação de gravação, título deliberado e escolhas preservados, sem falso conflito. A leitura pública comprova o transporte no destino; a correcção do rascunho ainda aguarda instalação/conferência desse delta na homologação. Sem consulta de credenciais ou bypass de sessão/RLS.
2. **Proveniência do relatório.** A origem existente por campo estava no dossier, mas não viajava no snapshot usado pelo relatório; as linhas tinham atribuições genéricas fixas ao utilizador. O snapshot passa a incluir apenas tipo/título/versão/revisão da proveniência, sem IDs de actores. O modelo comum de relatório/CSV usa também as assinaturas de herança já existentes: mostra origem na oportunidade/catálogo/dados do utilizador no dossier; uma alteração posterior no simulador é identificada com a origem anterior. Não muda valores nem reescreve relatórios emitidos; metadados ausentes de versões antigas não são inventados. Conferido no relatório QA: resumo herdado da oportunidade, título alterado posteriormente e referência da oportunidade/localização/associação conservadas. Captura `outputs/preservation-inventory-20261023/corrected-provenance-report-QA.png`.
3. **Beira → oportunidades.** A consulta da carteira só procurava pivots; o cadastro das 34 conserva a pertença documental no `workflow_context`, deliberadamente sem inferir relações com mestres. As fontes aceites distinguem COR-02 «Corredor da Beira e Centro» (`investment-portfolio.json`) e o grupo «Corredor da Beira» (`corridor-investment-opportunities.json`): sete entradas, não selecção por província. Esta correcção implementa uma referência documental delimitada entre essa ficha e esse grupo, sem renomear/fundir corredores, acrescentar pivots ou inferir implantação. A consulta exige a chave original e o hash da fonte aceite; outras oportunidades/corredores não entram por semelhança. A mesma resolução serve a ficha, a pesquisa por corredor, a validação da origem no detalhe e a associação escolhida no dossier. As sete fichas são Milho, trigo e arroz; Soja; Hortícolas; Sésamo; Açúcar; Florestas; Carne bovina — todas no Corredor da Beira. Não se estende esta correspondência aos outros corredores, clusters ou âncoras por inferência.
Validação delimitada: 26 testes frontend afectados passaram; novo teste de proveniência/ETag repetido após o ajuste de designações, dois aprovados. Três testes transaccionais de continuidade/Beira, 91 asserções, na base QA `simulator_fiscal_delta_20261021_test:55441`, incluindo sete resultados, pesquisa, detalhe/origem e recusa de outra oportunidade. Um teste adicional, cinco asserções, confirma que o servidor continua a recusar ETag fraco e versão antiga e aceita apenas a versão forte actual. Types/build aprovados. A conferência visual final da navegação da carteira QA foi interrompida pela automação do navegador; não se declara uma prova visual nova dessa ficha, coberta aqui pela resposta Inertia/API e validação de continuidade. Mantêm-se as provas anteriores. Matriz existente: evidências acrescidas em AUD-SIM-INT03/06/07, sem novo backlog. Aplicação transversal permanece parcial.
## Verificação do produto na homologação — 23/10/2026
Origem: pedido da Tempo de avaliar carteira → escolha → dossier → simulador → relatório, registando apenas diferenças e reutilizando as provas aceites. Revisão servida confirmada: `7c1f5321`. Sem alteração de código, permissões, originais ou publicações nesta passagem. Referências da matriz existente: AUD-SIM-INT03 (recuperação), AUD-SIM-INT06 (relatórios) e AUD-SIM-INT07 (gravação/erros).
Criado pela interface um dossier privado explicitamente denominado **TESTE DE HOMOLOGAÇÃO — continuidade 23-10-2026**, `958df138-7980-4685-8389-fa90be02ed6b`, rascunho do simulador `6c07a968-7ff9-42fa-b252-072bb1acf221`. Cenário sintético, não investimento real nem manifestação/submissão. Os dossiers anteriores permaneceram sem edição. A escolha Florestas — Corredor da Beira foi recebida; o corredor já vinha seleccionado e apresentou apenas Manica/Sofala. Escolhida Sofala e actividade florestal para o teste, a prévia propôs resumo/sector/localização e preservou o título deliberado do teste. Após confirmação, oportunidade, Agronegócio, Corredor da Beira e Sofala recuperados no dossier e no simulador sem preenchimento repetido. Necessidade de terra Não, identificada como cenário sintético: acção Terra/DUAT e etapa de regularização retiradas do dossier; Situação do terreno ocultada no simulador com explicação. Não se repete a prova de novo login já aceite em QA.
Diferenças observadas, sem declarar causa ou correcção não demonstradas:
- **Gravação/recuperação (AUD-SIM-INT03/07):** no novo rascunho e num único separador desta verificação, após escolher Simples/Português e navegar nas etapas surgiu «alterado noutro separador». Recuperar a versão mais recente conservou Simples e os dados herdados, mas não o idioma. Escolher novamente o idioma e Guardar e continuar mais tarde voltou a indicar gravação não confirmada/conflito. Não há outro separador deste dossier de teste; não atribuir a falha ao utilizador nem afirmar causa técnica confirmada. Não contornado o controlo de versão.
- **Origem no relatório (AUD-SIM-INT06):** o relatório imprimível contém o resumo e Agronegócio herdados, mas classifica-os como declarados/seleccionados pelo utilizador. A proveniência apresentada deve distinguir herança da oportunidade de edição deliberada. A selecção do território foi efectivamente feita no teste.
- **Carteira → escolha:** a ficha Corredor da Beira e Centro abre, mas informa não ter oportunidades publicadas associadas nem relações disponíveis; por essa ficha não foi possível demonstrar a passagem contextual à oportunidade. Florestas também apresenta ausência de relações publicadas. A falta de associações já estava identificada; esta observação localiza o limite no destino, não prova perda recente nem autoriza inferir/recriar ligações. O dossier antigo Florestas `90110be6-00c1-4e37-a82f-19e734f4e097` abriu sem oportunidade/sector/localização confirmados; o título não prova esses dados guardados. Foi preservado.
**Relatório:** a opção Imprimir / guardar como PDF gerou uma nova apresentação, lida no navegador. Inclui as onze secções seleccionadas, oportunidade/Sofala, necessidade de terra Não e preparação do terreno não aplicável, lacunas como Não informado e ausência de plano/cálculo sem zeros presumidos. Cabeçalho/tabelas legíveis na captura; não se declara nova conferência de paginação PDF ou de todas as páginas impressas. O relatório foi gerado com o aviso de gravação activo: não atesta que todas as últimas edições locais estavam persistidas. A tentativa de captura de download Word pela automação não terminou; isso não basta para declarar defeito do exportador nem download/abertura Word demonstrados. As provas QA desses formatos mantêm-se, sem substituir a lacuna observada de gravação.
**Correcção de saldo das 34 oportunidades:** a pendência de disponibilização não corresponde ao destino actual. O catálogo mostra as entradas; a exportação pública Agronegócio devolveu 34 referências, todas coincidentes por URL com `agribusiness-homologation-20261023.json`, zero ausentes. Não houve reimportação, nova publicação ou conferência individual de todas as páginas. Disponibilidade pública e associações à carteira são estados distintos.
Provas locais: `outputs/preservation-inventory-20261023/product-report-homologation-20261023.png` e `product-simulator-save-conflict-homologation-20261023.png`. Saldo: herança, localização e não aplicabilidade demonstradas neste cenário instalado; gravação/recuperação com falha nova; proveniência do relatório a corrigir; passagem por associações reais da carteira não demonstrada neste recorte. Aplicação transversal parcial. Nenhum fecho global do produto.
## Retoma e recuperação da continuidade — conferência QA, 23/10/2026
**Estado posterior: instalada na homologação.** A revisão `7c1f5321`, que inclui `6948ed62`, `3d877ec7`, `115c7731` e a correcção histórica do relatório, está efectivamente servida desde 23/10/2026 às 13:16 UTC. Recibo: `adaptive-portfolio-installation-receipt-20261023.json`. Conferidos revisão pública, assets do delta, hashes dos cinco ficheiros afectados e carteira anónima: 29 fichas, zero IDs ausentes e zero títulos divergentes. Executor: 204/204 testes frontend; zero migrations/importações e nenhuma alteração de permissões. Reutilizadas as provas privadas QA; não se gravou um dossier real no destino. As menções abaixo a homologação inalterada conservam o estado histórico da preparação QA, agora sucedido por este recibo. Aplicação transversal permanece parcial.
Disponibilização autorizada pela Tempo: `115c7731` integrado em `main` por avanço directo, sem alterações concorrentes no remoto, migrations ou alterações de dependências/permissões. A primeira execução automática preservou a release anterior `e1f010f9`: 203/204 testes frontend passaram; o teste do documento histórico não aplicável detectou que a simplificação do relatório ocultava também esse registo. Correcção delimitada: manter a linha histórica quando existe documento associado, sem voltar a exigir o requisito; os restantes não aplicáveis conservam a explicação breve. Verificados 17 testes afectados de relatórios, continuidade e terra, mais types. Não foram repetidas as provas funcionais QA nem alterados dados no destino. A integração Git não é recibo de instalação; confirmar a revisão servida antes de declarar disponível.
Ponto de retoma da Tempo: 29 fichas visíveis; completar a continuidade, sem repetir a reposição ou as provas aceites. A implementação `3d877ec7` sucede a `6948ed62` e permanece exclusivamente em QA. As secções históricas abaixo que identificam a perda de `from_dossier` descrevem o diagnóstico anterior: essa lacuna está corrigida neste recorte QA, ainda não na homologação.
Conferência pela interface, apenas com conta e registos SYNTHETIC QA: a oportunidade, Sofala e a associação escolhida chegaram ao dossier e ao simulador. Foi guardado no simulador o título deliberadamente diferente «SYNTHETIC QA — edição deliberada de continuidade». Regressar ao dossier e reabrir Plano e simulador conservou esse título; recarregar também o conservou. Depois de Terminar sessão e efectuar novo login normal, abriu-se o mesmo investimento pelo painel e o seu Plano e simulador: oportunidade, localização, associação e título deliberado foram recuperados, sem novo preenchimento. O título do dossier e o da oportunidade não foram substituídos pela edição do simulador. O login conduziu ao painel; a recuperação foi concluída pela ligação do investimento nesse painel, não se afirma redireccionamento automático ao simulador.
Prova local: `outputs/preservation-inventory-20261023/portfolio-continuity-recovery-login-QA.png`. O dossier sintético usado é `b5248bb8-5ada-445f-a34a-e4c628b6d457`; o rascunho recuperado é `b56a6bb8-c5df-4f22-84d0-07534d9dd8ba`. Nenhum dado real ou permissão foi alterado para esta conferência.
Relatórios: reutilizadas as provas aceites de `tests/frontend/investment-portfolio-continuity.test.mjs` para o modelo comum de relatório/CSV/impressão, e as provas de aplicabilidade e situação do terreno da entrega reconciliada. Não se declara uma nova exportação visual nesta passagem nem se repete a suite. A causa e prevenção da perda anterior permanecem na secção seguinte e no manifesto/recibo existentes. Homologação inalterada; aplicação transversal ainda parcial; associações ausentes não foram inventadas.
## Continuidade carteira → oportunidade → dossier → simulador — incremento QA, 23/10/2026
Origem: pedido expresso da Tempo de conservar oportunidade, localização e associações escolhidas no mesmo percurso, sem repetir perguntas ou sobrescrever edições deliberadas. Complementa a entrega QA `6948ed62`; não instala esse conjunto nem este incremento na homologação.
Os dois botões Preparar investimento da oportunidade passam a transportar a origem `from_dossier` já validada e os filtros territoriais escolhidos. A entrada mostra a oportunidade, localização proposta e associação da carteira antes de escolher/criar o dossier. As mesmas referências atravessam autenticação, rascunho por actor, recuperação, prévia, confirmação e a referência guardada do simulador. Permanecem no payload cifrado existente: sem migrations, papéis ou percurso paralelo. Posto/localidade não são inferidos quando faltam.
Cada oportunidade conserva as suas próprias associações escolhidas (`portfolio_origins`), com identificador, título, versão, ligação e hash documental. O servidor só aceita fichas publicadas da carteira com ligação cadastrada à oportunidade. Não cria relações por semelhança de nome, corredor ou proximidade e não publica fichas privadas. É possível retirar uma associação pela interface sem retirar a oportunidade. Uma mesma associação pode acompanhar oportunidades diferentes, mantendo cada identidade e localização.
Guia: abra uma oportunidade associada a partir da carteira e escolha Preparar investimento. Na entrada, confirme visualmente a oportunidade, localização recebida e associação; continue um dossier existente ou crie um novo. Reveja e guarde no editor existente. A confirmação retira da URL a intenção de entrada já consumida, para que actualizar recupere o contexto guardado, incluindo associações retiradas, em vez de reaplicar essa entrada. Abra Plano e simulador no mesmo dossier; os dados e referências continuam disponíveis, com os controlos existentes para editar divergências e exportar.
Ao retomar uma oportunidade já existente num dossier, acrescentam-se as associações recebidas sem substituir localização, corredor ou actividades guardadas. Uma localização recebida diferente é apresentada como divergência, mantendo a anterior para revisão explícita. Confirmar associações gera a revisão/auditoria do contexto existente; alterar apenas a referência documental não reabre etapas nem invalida cálculos se as entradas efectivas não mudarem. A alteração de localização, actividade ou outros pressupostos conserva os controlos de impacto existentes.
No simulador, o componente comum mostra as oportunidades, localizações e associações do dossier. Os campos recuperados continuam a usar os resumos editáveis da entrega anterior; respostas deliberadas do simulador não são substituídas. Relatórios e CSV do plano conservam cada oportunidade, o respectivo território e as associações escolhidas, com origem identificável, sem inferir incentivos ou autorização de execução.
Verificação do delta e regressão directamente afectada: `InvestmentPortfolioContinuityTest` e `InvestmentOpportunityChoicesTest`, seis testes/151 asserções em base QA isolada com transacções revertidas; incluem retoma após autenticação, selecção múltipla, associação partilhada entre fichas, preservação de título/localização/actividades, rejeição de origem não relacionada/privada, confirmação e recuperação no rascunho do simulador, e alteração só de referência sem invalidar cálculos. O teste de transporte dos 34 usa agora o mesmo agrupamento `selection_labels` do navegador, sem alargar os campos aceites pela API. Frontend: cinco testes territoriais/entrada e um teste de continuidade/CSV/impressão. Types e build aprovados. As provas dos incrementos anteriores mantêm-se; não se voltou a importar, publicar ou conferir os 20 artigos, 34 oportunidades, 408 limites ou 29 fichas na homologação.
Navegador QA: fixture identificada como SYNTHETIC QA, com oportunidade, âncora/associação e dossier próprios, sem atribuir a dados reais as associações do teste. Conferidos os parâmetros dos dois botões públicos, a recepção na entrada, escolha do dossier existente, rascunho guardado, prévia com Sofala/associação, confirmação e recuperação no simulador com a mesma referência. Os registos reais QA não foram reclassificados nem associados artificialmente para esta prova visual. Captura local `outputs/preservation-inventory-20261023/portfolio-dossier-simulator-continuity-QA.png`. Nenhuma escrita remota ou publicação real.
**Causa e prevenção da perda anterior:** mantém-se o diagnóstico documental de `e65cb269` e o manifesto nominal da reposição auditada. A mudança do importador para rascunhos deixou de reconciliar a divulgação anteriormente aprovada noutro ambiente; não se conhece a data exacta da perda num endereço. Nas próximas entregas, conservar a visibilidade/aprovação dos exemplares existentes e conferir os IDs/títulos/estados contra o inventário aceite, antes/depois do transporte; um transporte privado não revoga a divulgação anterior. Não restaurar a base inteira nem reimportar/publicar automaticamente conteúdos novos para corrigir visibilidade. Esta é prevenção técnica conforme a instrução de preservação da Tempo, não um novo aprovador. A presença visual não substitui o manifesto, a proveniência e o recibo de reposição existentes.
Saldo: visibilidade das 29 fichas confirmada no destino na passagem anterior; continuidade implementada neste recorte QA para associações efectivamente cadastradas. Fichas sem oportunidades associadas continuam a indicar essa lacuna; não foram preenchidas com relações presumidas. Homologação inalterada nesta passagem; aplicação transversal permanece parcial.
## Entrega QA reconciliada — preparação adaptativa, 23/10/2026
Conjunto funcional único, na ordem Git já existente: `7ca94967` → `f2150b19` → `5bda74ae`. A reconciliação documental `7d798579` conserva a evidência da carteira. Não fazer três instalações independentes, reaplicar patches ou substituir ficheiros por exemplares de cada candidata. Preservar estes commits e integrar o conjunto pelo processo Git de revisão existente.
| Candidata | Comportamento incluído | Provas aceites reutilizadas |
| --- | --- | --- |
| `7ca94967` | Ocultar preparação sem aplicação e respectivas acções; conservar estados e evidências; distinguir requisitos não aplicáveis nos relatórios | Teste API de preservação/reabertura: 18 asserções; relatório: 11 testes; types/build e cenário QA |
| `f2150b19` | Dossier → simulador: resumos editáveis dos dados recuperados, nomes territoriais ligados aos IDs correctos, edições deliberadas preservadas, percurso de análise existente | Reutilização: 12 testes; nomes exactos: 14 asserções API; types/build e gravação/recuperação visual QA |
| `5bda74ae` | Situação do terreno acompanha Não/Sim/Por definir na interface, gravação/validação, recuperação e relatórios; resposta anterior conservada | Delta de terreno: 4 testes frontend e 32 asserções API; types/build e três estados no navegador QA |
**Reconciliação Git:** base `origin/main` consultada novamente no remoto: `e1f010f941e9993645e131b34ec1f4c3fe82c71b`. As três candidatas são antecessoras do conjunto reconciliado; não há commits exclusivos de `main` a resolver nesta conferência. Os quatro worktrees existentes estavam limpos e os dois worktrees de outras entregas permaneceram intocados. Conferido o delta acumulado sem erros de whitespace; sem migrations, novas rotas ou alterações de dependências. Não foi reescrito histórico nem repetida a suite QA aceite. Esta conferência de integração não é nova prova funcional de todo o portal.
**Disponibilização e reversão:** esta entrega permanece em QA, sem integração em `main` ou deploy nesta passagem. Instalação futura usa o build e o deploy Git existentes; não requer importação, migração de dados, publicação de conteúdos ou novos papéis. Reversão de código deve conservar o payload cifrado, decisões, respostas anteriores, referências, relatórios emitidos e histórico; não restaurar uma base antiga nem eliminar campos/registos para reverter a interface. Conferir apenas o delta autorizado no destino quando a entrega for integrada.
**Formulação do saldo:** Homologação inalterada por esta entrega; aplicação transversal ainda parcial. A carteira e os projectos âncora permanecem prioritários no mesmo plano de estabilização. A visibilidade das 29 fichas foi confirmada contra o manifesto aprovado; a continuidade carteira → oportunidade → dossier → simulador permanece por concluir. Não declarar «sem reposição confirmada» para a visibilidade, pois isso contradiz a evidência registada e a conferência de leitura de `7d798579`. Não considerar encerrada a estabilização integrada por causa dessa visibilidade ou dos incrementos do simulador.
Pendência concreta preservada na matriz: transportar a origem `from_dossier` ao escolher Preparar investimento, sem presumir associações inexistentes. Nenhuma correcção dessa lacuna foi incluída silenciosamente neste conjunto.
## Carteira no percurso integrado — reconciliação do saldo, 23/10/2026
Decisão expressa da Tempo: carteira/projectos âncora e percurso adaptativo têm diagnósticos distintos, mas a separação não significa adiamento nem exclusão do percurso. A experiência deve conservar o contexto em corredor/localização → oportunidades → associações documentadas a âncoras e clusters → dossier → simulador. A recuperação da visibilidade é prioritária até confirmação da causa e da reposição; os incrementos adaptativos não constituem prova dessa reposição.
**Correcção do saldo comunicado nos últimos incrementos:** a causa e a reposição já estavam registadas na secção Preservação do concluído e no recibo existente. O importador passou a criar rascunhos sem reconciliar a aprovação documental anterior; o delta de `e65cb269` em `ImportInvestmentPortfolio.php` confirma a retirada de `--approval` e do caminho de publicação previamente aprovado. Não se atribui a essa alteração a data exacta de desaparecimento num endereço. A reposição anterior foi nominal, auditada e preservou relações/conteúdo: 15 projectos, 8 clusters, 6 corredores.
**Conferência actual de leitura na homologação:** a rota pública `/carteira` apresenta 29 fichas. Os 29 identificadores e títulos coincidem integralmente com `approved-portfolio-visibility-20261023.json`: zero ausentes e zero títulos divergentes. Contagem apresentada: 15 projectos, 8 clusters, 6 corredores. A ficha Cadeia de Valor do Arroz por Projecto de Processamento abre em `/carteira/57a4ee7a-4c07-4e26-b8f5-e07726e41512`. A sessão do navegador estava autenticada, mas foi consultada a rota pública, cujo controlador aplica a mesma consulta `visible()`; a prova anónima da reposição anterior mantém-se no recibo. Não foram executadas novas publicações, importações, escritas remotas ou reposições de dados nesta conferência.
**Continuidade ainda por concluir, distinta de desaparecimento:** a ficha de arroz consultada informa não ter oportunidades publicadas associadas. Não inferir ligações por semelhança de corredor ou cadeia de valor. No código actual, a carteira transmite `from_dossier` para uma oportunidade e a descoberta valida a origem; a ligação Preparar investimento em `investment/detail.tsx` transmite apenas `source=opportunity:{id}`. Falta preservar explicitamente essa origem documental no arranque/contexto privado. A presença das 29 fichas não demonstra essa continuidade até ao simulador, nem encerra a aplicação transversal. Manter este ponto na matriz existente e reaproveitar o percurso, sem criar cadastros paralelos ou novas associações presumidas.
## Situação do terreno acompanha a necessidade de terra — delta QA, 23/10/2026
Origem: decisão expressa da Tempo após `f2150b19`. No simulador ligado ao mesmo dossier, **Não** oculta Situação do terreno sem a exigir; **Sim** apresenta o campo com o valor já guardado e permite alterá-lo; **Por definir** identifica a pendência e permite guardar sem resposta. O simulador autónomo conserva o comportamento anterior. Não se infere a necessidade de terra da localização, do corredor ou da escolha de um documento.
A decisão vem de `investor_path.facts.land_needed`, já existente. O contexto de preparação transporta-a na referência guardada do simulador; versões anteriores recuperam a aplicabilidade S10. Informação ausente ou inválida permanece por definir. Arrendamento/DUAT já declarados no dossier podem preencher a situação correspondente quando a necessidade é Sim, sem sobrescrever uma edição deliberada no simulador. A área das instalações e os requisitos documentais/jurídicos não são alterados por inferência.
Gravação e validação: Situação do terreno continua opcional, sem criar obrigatoriedade para Sim nem para Por definir. Quando fornecida, conserva a validação das opções existentes. Na actualização do rascunho, o servidor consulta o dossier real vinculado ao seu titular, mesmo sem o parâmetro `context`, e conserva o valor e o nome anteriores enquanto o campo estiver oculto. A decisão guardada na referência é sincronizada com esse dossier; uma referência antiga não muda a decisão real. Recuperar Sim volta a mostrar o mesmo valor, editável. Valores anteriores permanecem no rascunho cifrado; fontes, decisões, resultados históricos e relatórios já emitidos não são apagados ou reescritos. Sem migration e sem alteração na homologação.
Relatórios e CSV do plano usam a mesma decisão: Sim apresenta a situação declarada ou a informação em falta; Não apresenta a explicação de não aplicabilidade à preparação actual, sem apresentar a resposta histórica como actual; Por definir apresenta a pendência. O modelo comum alimenta Word, impressão/PDF e restantes exportações do relatório. Os dados originais do rascunho permanecem conservados.
Verificação apenas do delta: quatro testes `investment-land-preparation.test.mjs` (três estados, recuperação, versão anterior, referência inválida, validação, CSV e impressão), um teste API em base QA isolada com 32 asserções (gravação incompleta, decisão real, omissão do campo, recuperação e alteração após Sim); types/build aprovados. No navegador, exclusivamente no dossier sintético QA: Não ocultou o campo; Sim permitiu guardar DUAT; Por definir mostrou a pendência e permitiu guardar; regressar a Sim recuperou DUAT seleccionado e editável. A cobertura transversal continua parcial. O saldo actual da carteira e da continuidade integrada encontra-se na reconciliação acima; este incremento não demonstra a reposição.
## Continuidade oportunidade → localização → dossier → simulador — incremento QA, 23/10/2026
Origem: decisão expressa da Tempo de aperfeiçoar o percurso adaptativo por incrementos. Mantém-se `7ca94967` como primeiro recorte QA; nenhuma destas duas alterações foi instalada na homologação nesta passagem. Diagnóstico de visibilidade e continuidade integrada da carteira: consultar a reconciliação acima; não os considerar adiados ou fora do percurso.
| Ponto encontrado | Delta neste incremento | Limite preservado |
| --- | --- | --- |
| Corredor/localização já escolhidos reapareciam no simulador como perguntas abertas | Reutilizar os mesmos identificadores e nomes do cadastro; mostrar resumos editáveis para valores herdados sem alteração local | Não inventar posto/localidade; manter as localizações específicas de todas as fichas. O simulador conserva o contexto geral da primeira escolha, sem agregar cálculos de oportunidades diferentes |
| Título, descrição e sector recuperados voltavam a pedir preenchimento | Resumo do valor recuperado e opção Alterar; falta de dado, erro de validação ou edição deliberada mantém a pergunta aberta | A gravação existente, as versões e a validação no servidor continuam a aplicar-se; não aceitar dados apenas porque o resumo foi mostrado |
| Entrar no simulador desde o dossier pedia novamente o tipo de percurso/finalidade | Entrada nova selecciona o percurso existente Avaliar o meu investimento / Apenas simular; opção Alterar disponível | Escolha técnica de navegação, não declaração jurídica. Não substituir uma finalidade previamente escolhida pelo utilizador; não escolher detalhe, perfil, regime fiscal ou respostas desconhecidas |
| Reutilização mostrava UUID, chaves e versões internas ao investidor | Componente comum apresenta nome do investimento, referências legíveis e regresso ao dossier | Identificadores, origem, versões e histórico permanecem nos registos; divergências não são resolvidas silenciosamente |
No simulador, os dados conhecidos aparecem como resumos com **Alterar**. Complete os restantes campos e use a gravação e o regresso ao dossier já existentes. Quando houver divergência, abra a comparação e seleccione expressamente os valores a substituir. Os nomes territoriais acompanham os identificadores correspondentes, incluindo após gravação/recuperação; uma escolha local divergente conserva o seu nome. Fontes sem nome recuperável não são mostradas como UUID no resumo.
Validação do delta: `context-inheritance.test.mjs` (12 testes, incluindo gravação/recuperação, edições/limpezas deliberadas, outro dossier, divergência geográfica e finalidade anterior); `InvestmentCorridorContextTest::test_simulator_reuses_names_for_the_exact_chosen_territory_and_sector_without_creating_a_location` (14 asserções, base QA isolada). A conferência visual utiliza exclusivamente o dossier sintético QA. A entrada pública/autenticação e a configuração de corredores reutilizam as provas aceites. Não se declara concluída a reutilização transversal em todos os consumidores; subsistem campos próprios do simulador e divergências a resolver pelo investidor, não dados a presumir.
Conferência visual concluída: entrada pelo mesmo dossier, percurso de análise seleccionado, título/resumo/sector como resumos editáveis, Sofala/Dondo recuperados, posto/localidade em falta sem preenchimento presumido. Uma edição deliberada do título no simulador sobreviveu à actualização da página; os restantes resumos continuaram herdados. Types e build passaram. A lacuna Situação do terreno identificada neste incremento foi tratada no delta QA descrito acima; o ocultamento de S10 em `7ca94967` não a tinha resolvido. Não alterar a aplicabilidade jurídica ou a área de instalações por inferência dessa resposta.
## Território adaptado ao corredor — decisão funcional da Tempo, 23/10/2026
Revisão funcional `0025309b` integrada pelo Git e disponível na homologação. Conferência do destino apenas de leitura: o serviço territorial para Maputo devolve exactamente as duas unidades existentes, sem consultar ou gravar um dossier real. A prova de recuperação e confirmação de Sofala/Dondo foi feita no dossier sintético QA, com indicação **Dados confirmados**. A prévia apresenta nomes e alterações úteis ao investidor, omitindo campos de identificadores internos.
Aplicada a tabela da Tempo como configuração do percurso existente (`config/investment_corridors.php`), usando os códigos e identificadores do cadastro territorial já aceite. Pemba–Lichinga: Cabo Delgado/Niassa; Nacala: Nampula; Vale do Zambeze: Tete/Zambézia; Beira: Sofala/Manica; Limpopo: Gaza; Maputo: **Maputo Província e Maputo Cidade**, conforme o esclarecimento final da Tempo. Só Nacala e Limpopo têm uma única província e a seleccionam automaticamente quando ainda não existe localização. Maputo exige escolher entre as duas unidades. Esta configuração funcional não declara novos limites geográficos nem altera os mestres, as associações ou as fichas publicadas.
Na preparação, cada oportunidade mantém a sua localização separada. O corredor inicial vem da fonte da ficha quando disponível; o investidor pode escolher outro corredor para o seu investimento, sem alterar a associação publicada da oportunidade. Uma mudança conserva o caminho ainda compatível; se a província deixar de pertencer ao corredor, retira os níveis territoriais dependentes e explica a alteração. A província única é proposta automaticamente; um território documentado ou previamente confirmado continua protegido pela confirmação de alternativa aplicável. A localização da primeira oportunidade alimenta os campos gerais existentes; outras escolhas permanecem identificadas separadamente.
Zonas e parques deste percurso só aparecem com território público documentado compatível, reutilizando a visibilidade, validade e vínculos existentes. Influência/proximidade não equivale a implantação. Uma indicação provincial de localização não é inventada como cobertura de todos os distritos; apenas âmbito legal documentado permite esse alargamento. Ausência de opções é distinta de erro de consulta, com nova tentativa. Ao mudar a localização, um parque que deixe de ser compatível é retirado e a razão aparece no formulário. Consulta não confirma lotes disponíveis, admissão ou benefícios; nenhum cálculo é activado.
Recuperação: o rascunho do formulário é guardado no payload cifrado do mesmo dossier, por actor, sem confirmar contexto, aumentar a versão funcional ou activar cálculos. Recupera escolhas, actividades e dados ao regressar, actualizar ou autenticar-se novamente. A gravação automática tem indicação de progresso/sucesso/erro e nova tentativa. Pedidos atrasados não substituem rascunhos mais recentes; conflito com alteração do dossier conserva o rascunho para revisão. Retirar uma ficha não a faz reaparecer ao actualizar a mesma entrada; referências novas vindas do catálogo continuam a ser acrescentadas. A confirmação normal guarda o contexto, preserva o histórico e retira o rascunho desse actor. Não há nova tabela, migration, papel, aprovador ou alteração da autorização existente.
Validação no servidor: recusa território fora do corredor tanto na prévia/confirmação como no rascunho; recusa parques sem compatibilidade documentada. Os filtros da API territorial aplicam a mesma configuração. A gravação protege o dossier e o actor, com controlo de concorrência, sem aceitar combinações só porque foram enviadas directamente à API.
Provas QA: suite de cinco testes de configuração, validação, recuperação/autenticação, conflitos, zonas e mudança de corredor — **90 asserções**; quatro testes de continuidade de oportunidades também passaram na passagem completa anterior. Testes usam exclusivamente `simulator_fiscal_delta_20261021_test:55441`, com transacções revertidas. Tipos e build passaram. Navegador com conta/dossier **sintéticos privados em QA**: Beira oferece Sofala/Manica; Nacala selecciona Nampula; Maputo oferece as duas unidades; Sofala/Dondo recuperado depois de actualizar. Captura: `outputs/preservation-inventory-20261023/corridor-adaptive-territory-QA.png`. Nenhum dossier real foi usado para provas de gravação. As 34 oportunidades, Turismo de Inhambane, permissões e geometrias não foram alterados por esta implementação.
## Continuidade entre escolha e preparação — 23/10/2026
Falha reportada pela Tempo: depois de criar o dossier para Florestas/Beira, o editor começava por uma avaliação do estado ainda não confirmado (`contexto v0`). A escolha solicitada vinha separadamente em `source`/`requestedChoices`; não era outro investimento, mas a avaliação do estado guardado vazio. A ligação do quadro voltava ao editor sem os parâmetros da escolha pendente, permitindo perder essa referência antes de confirmar. Não foi identificada nem corrigida por escrita uma troca de dados entre dossiers.
O editor passa a começar por **Preparar investimento**, pela oportunidade e pela localização por ficha. Retirado o quadro de avaliação do estado guardado deste ecrã de edição, incluindo a ligação que descartava a escolha pendente. Requisitos guardados só são apresentados quando correspondem às escolhas que estão no formulário. Botões coerentes: **Criar dossier e preparar investimento**, **Rever dados antes de guardar**, **Guardar dados do investimento**. A confirmação continua necessária para guardar alterações; abrir uma referência não altera o dossier.
Nos consumidores de avaliação, retirados números internos de contexto/versão, explicações de implementação, quadro genérico de ausência de zona e elementos sem título na lista de etapas. Mantidas condições úteis, fontes oficiais e limites efectivos do simulador. O exemplo real foi recuperado por GET com `source=opportunity:53` no dossier indicado pela Tempo, sem guardar ou criar dados. Teste QA isolado: 20 asserções sobre recuperação da escolha e igualdade do contexto antes/depois da abertura. Tipos e build conferidos; sem migrations ou alterações de cálculo, autorização ou dados activos.
Revisão funcional `72262875` integrada pelo Git e conferida no navegador da homologação: título **Preparar investimento**, Florestas/Beira identificada, localização por escolher e botão **Rever dados antes de guardar**; o quadro de contexto v0 já não aparece. Mantido aberto o dossier existente com a referência solicitada recuperada, sem confirmar alterações. Captura: `outputs/preservation-inventory-20261023/investment-preparation-continuity-homologation.png`.
## Dossier orientado ao investidor — 23/10/2026
Correcção da apresentação solicitada pela Tempo: o componente partilhado de documentos do contexto mostrava, por defeito, identidade técnica, versões, hashes, notas de permissões e mensagens de ausência de suporte. Era reutilizado na entrada, edição e consulta do dossier; a entrada repetia ainda a mesma oportunidade num bloco de origem técnica.
O investidor vê agora a escolha pelo título e respectiva ligação. **Documentos de apoio** só aparece quando existem documentos, com título, edição disponível, referência de secção e consulta do documento. Sem documentos, não se apresenta uma caixa vazia nem uma pendência genérica da biblioteca. Retirados também UUID, motor de cálculo, versões internas e chaves de origem dos resumos, prévia e consulta do dossier. Mantidas as informações operacionais relevantes: escolhas, localização, impactos da alteração, resultados que precisam de revisão, documentos e pedidos identificados por protocolo. As rotas e verificações de acesso são as mesmas; não há mudança de dados, histórico, fontes, permissões, cálculos ou publicações.
Tipos e build passaram. Revisão funcional `98474777` integrada pelo Git. Reproduzido o exemplo Florestas/Beira (`opportunity:53`) na homologação antes e depois da integração, sem criar ou alterar um dossier: agora apresenta apenas **A sua escolha** com o título e ligação, sem caixa vazia de documentos, versões ou chave técnica. Captura: `outputs/preservation-inventory-20261023/investor-dossier-clean-presentation-homologation.png`. Esta é uma correcção de apresentação nos componentes e páginas do percurso, não uma declaração de revisão integral do portal.
## Explorar oportunidades por corredor — 23/10/2026
Disponível e conferido no navegador da homologação, revisão funcional `20349299`: [explorar Agronegócio por corredor](https://teste-investidor.tempo.co.mz/oportunidades?sector=agronegocio&view=corridors). Conferidos os seis grupos, 34 resultados, acesso no início de Agronegócio e escolha dos IDs 54/64 de corredores diferentes, mantida depois do filtro Soja. [Recibo da passagem](agribusiness-corridor-explorer-20261023.json).
Decisão expressa da Tempo: acrescentar o corredor como forma de explorar o catálogo, preservando vistas por sector, território e cadeia de valor, fichas únicas e percurso existente. Nova vista `/oportunidades?sector=agronegocio&view=corridors`, com acesso destacado no início de Agronegócio. Não há migration, cadastro duplicado ou alteração de publicações, associações, permissões, Turismo de Inhambane ou geometrias.
Cada grupo apresenta nome, número de oportunidades publicadas, localizações identificadas, cadeias e etapas/actividades. O lote usa as seis designações exactas da fonte aceite; não as funde com mestres semelhantes. Outras fichas usam as relações públicas já disponibilizadas pelo catálogo; proximidade não é tratada como pertença. Sem associação pública identificada, a ficha aparece em **Outras localizações**. Uma ficha em vários grupos mantém o mesmo ID e a selecção é única. Contagens de grupos podem sobrepor-se; não significam cadastros distintos.
Dentro do grupo, filtrar província, distrito e cadeia de valor; filtros mantêm escolhas anteriores, inclusive de outros grupos. **Comparar as escolhidas** reutiliza a comparação existente de até quatro fichas; essa capacidade não limita a escolha para preparar investimento. **Preparar investimento com as oportunidades escolhidas** envia todos os IDs ao mesmo dossier, onde se confirma a localização por ficha e se reutilizam os dados em licenciamento, terra e simulação. Filtrar a pesquisa não altera a localização da ficha nem escolhe silenciosamente uma implantação.
Lacunas reais: as 34 fichas não têm província/distrito de implantação identificados; a vista indica abrangência por indicar e não transforma nomes de corredores em limites territoriais. As actividades produtivas são apresentadas com o texto da fonte, sem inventar uma classificação de etapa. Etapas adicionais vêm apenas das relações públicas existentes. Os originais e a rastreabilidade permanecem internos.
Delta conferido: dois testes, 26 asserções (seis grupos e 34 IDs sem truncar à primeira página; relação múltipla com um ID; grupo residual; localizações conhecidas sem inventar faltantes). Build e tipos verificados. Navegador QA: escolha de duas fichas de Beira e Maputo, filtro Soja mantendo as escolhas e comparação das duas identidades. Reutilizadas as provas aceites da confirmação territorial e continuidade do dossier.
## Apresentação sem notas internas — 23/10/2026
Correcção solicitada pela Tempo: a interface deve comunicar a tarefa e o conteúdo, sem narrar decisões de implementação. Simplificados o cabeçalho da gestão do lote, a entrada no roteiro e as escolhas de oportunidades. Na ficha pública, removido o alerta genérico de falta de data de validação; mantém-se o estado **Em preparação** e a indicação de que a execução depende das condições e autorizações aplicáveis.
A apresentação das 34 fichas elimina exclusivamente os parágrafos automáticos conhecidos de nome de ficheiro, hash e metadados vazios. Conserva a actividade, o corredor e a informação útil sobre definição de localização e condições. A mesma apresentação é usada na serialização HTML/API e na prévia. Não há alteração de dados, fontes, versões, permissões, auditoria ou publicação; textos posteriormente revistos pela equipa são preservados. As notas e originais continuam no cadastro interno. Esta correcção não declara revista a totalidade das páginas do portal.
Verificação: teste novo de apresentação/preservação (7 asserções), quatro testes do ciclo promocional e controlo de acesso passaram; tipos e build passaram; ficha conferida no navegador QA. A execução alargada encontrou três testes históricos incompatíveis com o estado actual da fixture (lote já divulgado e directório de contas excluído da candidata anterior); não são declarados aprovados nesta passagem nem foram alterados para ocultar essas diferenças.
## Orientação do lote de Agronegócio — catálogo e selecção múltipla, 23/10/2026
**Homologacao concluida, 23/10/2026:** 34/34 fichas cadastradas e divulgadas como **Em preparação**, pela interface da conta real, com previa, motivo, confirmacao e MFA recente. Revisao servida `e12ef6ad426ff6ebc4e7421a1ddbe7f184d77a55` (funcional `c641b267`), integrada pelo Git, sem migration. [Catalogo publico](https://teste-investidor.tempo.co.mz/oportunidades?sector=agronegocio), [gestao unica](https://teste-investidor.tempo.co.mz/gestao-conteudos) e [gestao do lote](https://teste-investidor.tempo.co.mz/minhas-oportunidades/lotes/agronegocio). A divulgacao nao autoriza execucao. [Recibo final](agribusiness-homologation-20261023.json).
**Delta final:** IDs 31-64, sem duplicados; 34 resultados e escolha de duas fichas, IDs 64 e 63, no percurso existente. As 30 oportunidades anteriores, incluindo Turismo de Inhambane, permanecem exactamente iguais ao backup. Das 174 tabelas, 170 mantem checksum integral; alteracoes apenas em oportunidades (+34), auditoria (+102 eventos), cache e sessoes. Biblioteca (59 recursos), 408 geometrias, permissoes e simulacoes preservadas. Backup/restauro isolado verificado antes da operacao. Cadastro, edicao, recuperacao, previa, divulgacao, actualizacao e retirada usam o portal, sem comandos ou Codex nas operacoes correntes. Reutilizadas as provas QA do percurso, regras e historico; sem dossiers ficticios na homologacao.
**Preservação conferida antes do cadastro:** 172/174 tabelas iguais ao backup; apenas cache e sessões diferem. As 30 oportunidades preexistentes, biblioteca com 59 recursos, 408 geometrias, permissões e tabelas de simulação permanecem preservadas. A primeira comparação da aplicação, sem contexto de actor, mostrava vazias as tabelas de simulação sujeitas a RLS e originou um alerta incorrecto. A comparação posterior usou a mesma identidade de leitura do backup e confirmou igualdade integral dessas tabelas. Nenhuma reposição, eliminação ou alteração de política foi executada; a pausa técnica do observador foi desfeita e o deploy está activo.
Decisão expressa da Tempo nesta passagem: publicar as 34 fichas no catálogo de Agronegócio e permitir escolher uma ou várias, confirmar a localização por ficha, preparar o investimento com reutilização dos dados e consultar requisitos/documentos/entidades, simular e acompanhar no dossier autenticado. Corredores, clusters e projectos âncora enriquecem a análise e não são pré-condição de publicação; localização não activa benefícios fiscais. Não alterar Turismo de Inhambane. Esta orientação supera o estado privado do recibo QA anterior, conservado como histórico.
**Conferência do existente e delta:** o catálogo já comparava quatro fichas e o dossier já tinha 19 etapas, prévia/confirmação de contexto, mapas, reutilização no simulador/terra/Balcão, pedidos e histórico. Faltava transportar uma escolha múltipla identificada para esse mesmo dossier. Acrescentado `opportunity_choices` no contexto cifrado existente, sem tabela, migration, novo dossier paralelo ou novo papel. Cada escolha conserva ID, título, versão/hash, fonte, corredor documental e referência territorial própria. A escolha do catálogo persiste ao mudar de página; ao continuar um dossier, referências novas são acrescentadas às existentes, sem eliminar silenciosamente as anteriores. A comparação de quatro fichas permanece independente da escolha de investimento. A guarda de 100 referências por pedido é uma escolha técnica de tamanho, não nova decisão organizacional.
**Percurso da equipa e do interessado:**
1. Na gestão única, abrir **Rever o lote das 34 oportunidades de Agronegócio** (`/minhas-oportunidades/lotes/agronegocio`). Conferir o manifesto e cadastrar/recuperar privadamente. Depois, preparar a prévia dos 34 textos, rever por ficha e confirmar a divulgação com motivo e MFA recente. A publicação não depende de relações com mestres. Edição, actualização ou retirada individual continuam na divulgação promocional existente.
2. No catálogo, marcar **Escolher para preparar investimento** em uma ou várias fichas e usar **Preparar investimento com as oportunidades escolhidas**. Entrar normalmente, continuar um dossier autorizado ou criar um novo. Confirmar cada ficha e a localização no editor do mesmo contexto.
3. Localização documentada é recuperada. Uma alternativa exige confirmação explícita e motivo; a ficha de origem fica intacta. Uma alternativa já confirmada não exige repetir a decisão enquanto se mantiver. Sem informação de implantação, escolher o nível territorial conhecido ou conservar a lacuna identificada; o mapa não inventa limite, parcela ou DUAT.
4. Declarar as actividades efectivas por ficha. A prévia e o dossier apresentam requisitos por oportunidade: localização/geolocalização, acesso à terra/DUAT, ambiente, enquadramento sectorial e ramos industrial/comercial quando declarados, com fundamento, documentos, entidade e ligação à etapa existente. Reutilizadas as condições e fontes da preparação do investimento privado. Fontes adicionais de consulta: [BAU — legislação](https://www.bau.gov.mz/legislacao/), [serviços BAU](https://www.bau.gov.mz/nossos-servicos/) e [quadro ambiental oficial](https://sibmoz.gov.mz/legal-framework/). Categoria ambiental, modalidade de licença e competência concreta dependem de actividade, dimensão e local; não são deduzidas de um corredor. Água, florestas, pecuária e transporte mantêm visível a necessidade do procedimento sectorial específico quando não identificado no acervo aplicado. Não é nova validação genérica da fonte nem dispensa de licença.
5. Confirmar a prévia. Título próprio e dados declarados são preservados; resumo/sector/local da referência principal são reutilizados. As outras fichas e localizações mantêm-se individualizadas: não se somam investimentos ou benefícios presumidos. Abrir as ferramentas e o acompanhamento pelas ligações do roteiro. O snapshot da simulação conserva o conjunto de escolhas, fontes e requisitos; a guarda textual foi aumentada de 20.000 para 1.000.000 caracteres para comportar o lote, sem mudança de schema. Mudança de actividade/local/escolha identifica resultados para revisão e preserva os valores e o histórico anteriores.
**Provas do delta QA:** sete testes específicos/de continuidade, 132 asserções; selecção múltipla, localizações distintas, recusa de alteração territorial silenciosa, não repetição da confirmação já aceite, recusa de fonte alterada após prévia, escopo de acesso, regras por actividade, histórico/resultados preservados e guarda das 34 escolhas no simulador. Prova adicional da operação de lote pela sessão HTTP normal: um teste, 15 asserções, incluindo manifesto divergente, prévia exacta de 34, repetição idempotente, recusa sem MFA/âmbito e preservação das restantes oportunidades. As 34 fichas foram efectivamente divulgadas **em QA**, como Em preparação; a simulação inicial de papéis QA não constitui autenticação real de homologação. Conferidos também no navegador 34 resultados e escolha de duas fichas com acção de preparação. Não repetidas as suites gerais aceites.
**Disponibilização no destino:** revisão servida inicial `761035ae`; 30 oportunidades preexistentes preservadas no inventário. Conta real de cadastro e organização operadora localizadas pelas designações existentes, sem criar papéis ou contas. Backup/restauro fresco em `/var/backups/investmoz-agribusiness-opportunities-20261023/backup`: 174 tabelas e 369 ficheiros conferidos. A integração Git de código é separada do cadastro/divulgação autenticados pela página do lote. Sem migrations ou importação de ficheiros privados; os originais e artigos existentes permanecem no armazenamento. Reversão de visibilidade usa retirada auditada pela interface, sem restaurar a base nem apagar manifestações/dossiers posteriores; reversão de código deve preservar o novo contexto cifrado e as referências adicionadas. A conferência final do destino e o recibo devem ser registados abaixo após execução.
## Correcção de âmbito — 34 oportunidades cadastradas e roteiro em QA, 23/10/2026
A lista informativa anteriormente instalada no artigo não cumpria o cadastro pedido pela Tempo. Esta correcção cria **34 fichas privadas reais na base QA isolada**, recuperáveis pela gestão existente, com título, resumo e descrição da entrada, sector Agronegócio, nome exacto do corredor documental, fonte e hash do DOCX. Conserva as seis secções e os totais 5/6/6/7/5/5. Não modifica a homologação, os corredores mestres, dossiers anteriores ou o original do artigo. Não transforma a organização operadora no promotor dos investimentos. Implantação, área, montantes, promotores, viabilidade e licenças ausentes ficam explicitamente por completar; nenhuma correspondência com corredores de nomes mais amplos foi inferida.
Reutilizada a implementação QA de divulgação promocional da candidata `9d3bfa89`, limitada ao fluxo funcional necessário. Não transportadas as alterações independentes da administração de contas. A prévia, divulgação, actualização e retirada usam as designações, âmbito e MFA existentes, sem novos papéis ou aprovadores. O resumo público é seleccionado explicitamente; preparação, contactos e documentos privados não são copiados automaticamente. Divulgação identifica **Em preparação** e não autoriza execução. Publicação documental e aprovação/publicação de uma ficha completa continuam operações distintas.
**Guia curto da equipa em QA:**
1. Abrir a [gestão única QA](http://127.0.0.1:18574/gestao-conteudos), entrar normalmente com a conta designada e abrir [Minhas oportunidades](http://127.0.0.1:18574/minhas-oportunidades). Pesquisar o título ou corredor; as fichas iniciais são privadas.
2. Abrir **Editar**, completar/corrigir os dados disponíveis, guardar e reabrir. A origem documental aparece separadamente; seleccionar localização apenas quando houver informação suficiente. A referência textual de corredor não é uma parcela.
3. Abrir **Divulgação promocional**, rever o texto público e a fonte, preparar a prévia e confirmar com motivo e MFA recente. A prévia conserva as lacunas e o estado **Em preparação**. Alterar a ficha privada não copia automaticamente dados para o resumo público: rever a selecção na divulgação.
4. Depois de divulgar, a entrada correspondente no artigo aponta para a ficha individual. Nela, o interessado pode preparar uma manifestação de interesse ou escolher **Iniciar ou continuar o roteiro deste investimento**. O percurso existente permite seleccionar um dossier ou criar um novo, comparar e confirmar a referência; preserva o título que o interessado já deu ao seu investimento.
5. Para corrigir o resumo divulgado ou retirá-lo, usar a mesma área de divulgação, com prévia e auditoria. A retirada remove a consulta pública e a ligação do artigo, preservando cadastro, preparação e histórico.
**Provas do delta:** dois testes sobre as 34 fichas, 534 asserções: abrir/editar, prévia, divulgação, ficha pública, origem de roteiro, correspondência das seis listas, actualização/retirada e repetição sem duplicados nem perda da edição. Teste adicional de interessado: 19 asserções, criação de dossier, comparação/confirmação da oportunidade, recuperação de título próprio/resumo/sector, abertura do roteiro e preparação da manifestação. Escritas desses testes revertidas por transacção: as **34 fichas permanecem privadas**, não divulgadas como efeito do cadastro. Conferidos por comparação os registos preexistentes de oportunidades/auditoria, corredores, permissões, biblioteca e documentos. Verificação de tipos e build passaram. Trata-se de prova HTTP dos percursos usados pela interface, sem afirmar uma nova sessão manual de navegador ou repetir as suites gerais aceites.
Carregamento inicial executado uma vez por `tools/register-corridor-agribusiness-qa.php`, com guarda exacta de base/porta QA, actor e organização já designados, snapshot privado e chaves estáveis por entrada. A repetição recupera os mesmos IDs sem sobrescrever edições posteriores ou reactivar uma retirada. Esse carregamento técnico de instalação não é uma operação corrente da equipa. **Editar, guardar/recuperar, pré-visualizar, divulgar, actualizar, retirar e iniciar/continuar o roteiro não exigem comandos nem Codex.** Não há migration ou nova dependência. Disponibilização na homologação e transporte das 34 fichas continuam separados desta entrega QA; não foram executados nem autorizados por esta correcção. [Recibo QA](agribusiness-opportunities-qa-20261023.json).
## Agronegócio nos corredores — complemento de apresentação, 23/10/2026
Entrega QA recebida em `entrega-agronegocio-corredores-20261023`: cinco ficheiros conferidos pelos hashes do manifesto. Complementa exclusivamente `/agronegocio`, nas seis secções existentes: Pemba–Lichinga (5), Nacala (6), Vale do Zambeze (6), Beira (7), Limpopo (5) e Maputo (5), total 34 entradas. Reutilizadas as provas QA entregues (dois testes, 63 asserções e verificador offline do exemplar preservado); não constituem prova HTTP do destino.
Delta: serviço `CorridorAgribusiness`, fonte JSON com hash do DOCX e chamada adicional em `LegacyContent::article`. O ficheiro anterior de `LegacyContent` na homologação corresponde exactamente à base local; não há alterações posteriores a sobrescrever. Sem migration, importação, escrita de conteúdo, publicação de dossiers ou alteração dos nomes/cadastros de corredores. Originais e versões da biblioteca permanecem preservados; estas entradas são conteúdo informativo do artigo, não 34 novos dossiers executáveis.
Disponibilização pelo Git e mecanismo automático existente, com cópia privada do código/recibo anteriores em `/var/backups/investmoz-agronegocio-corredores-20261023`. Backup/restauro de dados já verificados nesta sessão em `/var/backups/investmoz-preservation-inventory-20261023/backup`; esta entrega não escreve na base. Reversão delimitada: retirar apenas a chamada de complemento, preservando alterações posteriores; não restaurar a base de dados. Conferir apenas o delta HTTP após a promoção da revisão.
**Instalação concluída na homologação:** revisão funcional `20a4099d10400496e19f03589b6383f512b48d9d`, promoção automática terminada às 09h57 de 23/10/2026 (África/Johannesburgo). [Página pública](https://teste-investidor.tempo.co.mz/agronegocio) respondeu HTTP 200 com as seis listas e 34 entradas exactas. Remover apenas os acrescentos recupera exactamente o HTML anterior; imagem, metadados, origem, versão e hash do artigo permanecem iguais. Recibo do pipeline: zero migrations e zero importações. [Recibo do delta HTTP](agronegocio-corredores-installation-20261023.json). Nenhum dossier ou corredor foi criado, renomeado, fundido ou publicado por esta instalação.
### Registo da prova QA anterior — agronegócio nos seis corredores
**Registo histórico: a pendência de instalação indicada nesta prova foi resolvida pela conferência HTTP acima.**
Fonte fornecida pela Tempo: `investimentos nos corredores.docx`, SHA-256 `1473b06c48aa234e771660e3f2a51b9d2e4e1943bd52d878e0ae854ee6657055`. Extraídas 34 entradas, conservando o texto das oportunidades e as seis designações do documento: Pemba–Lichinga (5), Nacala (6), Vale do Zambeze (6), Beira (7), Limpopo (5) e Maputo (5). Preparação em `database/data/corridor-investment-opportunities.json`; os rótulos de cadeia servem apenas para organizar as entradas. Âmbito confirmado pela Tempo: **agronegócio**; outros sectores serão acrescentados quando a informação estiver disponível.
**Preservar os nomes dos corredores actualmente cadastrados, os dossiers, relações, aprovações e informações anteriores.** Retiradas as associações inicialmente inferidas entre os seis nomes do documento e os quatro dossiers de âmbito mais amplo; a apresentação experimental na carteira foi retirada. A Tempo confirmou expressamente as seis designações separadas. Foram localizadas, com essas designações exactas e as letras a)–f), nas secções do artigo existente `/agronegocio`. A integração acrescenta a cada secção a sua lista de oportunidades logo após o parágrafo de cadeias prioritárias; a fonte do complemento é identificada em cada lista. Não renomeia, funde ou cria corredores, não altera os dossiers da carteira e não preenche outros sectores.
`App\Services\CorridorAgribusiness` complementa apenas a apresentação do artigo de agronegócio a partir do JSON; o exemplar original e a sua versão/hash permanecem intactos. Só corresponde a nomes exactos, seguidos pelo parágrafo existente de cadeias prioritárias. Não infere relações, limites, geometrias, projectos ou oportunidades cadastradas a partir de semelhança de nomes. A operação é idempotente e não escreve na base de dados. Artigos de outros sectores continuam no percurso anterior.
Conferência do delta: dois testes unitários, 63 asserções, incluindo separação de listas, nomes e texto anterior preservados, repetição sem duplicação e recusa de nomes de âmbito mais amplo. Verificador offline `tools/verify-corridor-agribusiness.php` executado sobre o exemplar real de Agronegócio, SHA-256 `0929184a6d4645b9046512675394ad4f60575273f6dab0955f71cc7474a6a4e2`: seis listas, 34 entradas, original inalterado e recuperação exacta da apresentação anterior ao remover apenas os acrescentos. A leitura do artigo pela biblioteca desta base QA devolveu 404; por isso, esta prova é sobre o exemplar real preservado e não demonstra consulta HTTP da biblioteca nem instalação na homologação. Sem suite geral repetida, migrations, publicação ou alteração remota. Para instalar basta o delta PHP/JSON; não há alteração de frontend ou necessidade de nova compilação. Estado: implementação QA concluída; disponibilização na homologação por executar.
## Preservação do concluído — inventário e reposição, 23/10/2026
**Prioridade expressa da Tempo:** inventariar o que já estava concluído, identificar o que desapareceu e repor, antes de avançar com outras rondas de testes/validações. Actualizado o [inventário existente dos 43 componentes](#saldo-dos-43-componentes) com uma coluna de saldo actual; conservada também a matriz executável dos 17 percursos. Os componentes de interface ali referenciados existem no código actual. As entregas posteriores prevalecem sobre estados históricos: por exemplo, os incrementos 36–39 já não podem ser apresentados como ausentes, e o acervo editorial já não está genericamente desabilitado. Não se criou outra matriz/backlog nem se usou o inventário para exigir novamente aceitações anteriores.
**Recortes concluídos a preservar**, com os limites e recibos das secções próprias: gestão única e editorial documental; 20 artigos públicos e 28 rascunhos privados; sectores/Fintech e cartões educativos; navegação contextual e percurso adaptativo privado; recortes entregues de parques; cadastro e recursos das oportunidades; divulgação dos 30 resumos Inhambane em preparação; perfis/login/MFA; demarcação recuperada; recortes de tesouraria/exportações e consumidor IVA já entregues; instalação de Concursos e anúncios; e carteira documental agora reposta. A presença de código não encerra os critérios parciais de educação, integrações externas, participantes reais ou variantes fiscais. Não se criam dados de exemplo para preencher catálogos vazios, nem se transformam essas pendências em funcionalidades desaparecidas.
**Perdas e reposições identificadas:**
- **Carteira, clusters, corredores e projectos âncora:** 29 dossiers existiam em QA e homologação, todos em rascunho, enquanto os exemplares anteriores estavam publicados com aprovação `document_owner`. Reposta a visibilidade de **15 projectos, 8 clusters e 6 corredores**, sem nova importação. O catálogo público mostra novamente os 29 dossiers. Isto é divulgação documental informativa, não confirmação de execução, disponibilidade, financiamento ou promotor de cada projecto.
- **Demarcação territorial:** a diferença anterior de geometrias já foi reposta em 22/10; a leitura actual conserva **408 limites**. Não voltar a importar nem reabrir a prova aceite. As 26+147 correspondências pendentes e a inexistência de polígonos próprios de localidades permanecem lacunas reais, não novos desaparecimentos.
**Causa delimitada da carteira:** o importador actual cria os dossiers como rascunhos. O commit `e65cb26901a5ca18862134b20e4c17b1f1003ec2`, de 06/10/2026, retirou a opção `portfolio:import --approval` e o caminho que aplicava a aprovação documental do proprietário. A instalação noutro ambiente deixou de transportar/reconciliar essa decisão de divulgação. A leitura de 23/10 encontrou os 29 registos, 16 projectos, 8 clusters e 6 corredores preservados; não ocorreu eliminação desses mestres. Conteúdo, título e fonte dos 29 dossiers coincidem com os exemplares aprovados em 26/09 e conservados no snapshot de 07/10. Não há base para atribuir a um commit recente a data exacta em que desapareceram de um endereço específico.
**Reposição executada:** manifesto nominal de 29 originais, SHA-256 `8d07007a2e1e8758d4741b4ac594f78d5943f71ad78b2daae7f85aefa9d2de4a`; guarda contra exemplares arquivados, editados ou com versão/hash divergentes; reposição primeiro na base QA isolada e depois na homologação, pela sessão SSH autenticada. Mantidos IDs, conteúdos, fontes, mestres e relações. Apenas estado de divulgação, fundamento anterior, versão e data da reposição foram actualizados. **29 eventos auditados**, sem atribuir uma nova aprovação ou um revisor fictício; a evidência original da Tempo é conservada. `tools/restore-approved-portfolio-visibility.php` é uma operação de recuperação delimitada, não um importador que publica automaticamente novos conteúdos e não uma tarefa editorial corrente.
Antes da escrita activa: backup fresco SHA-256 `7b7a3a082e1259c53c321617eb84d10b544e5a211f5f708199026c4205d1c511`; arquivo de ficheiros/configuração `0cc28141a081ce9d097735c7815b46f55d99300adf7361d936b857a24f897b86`; restauro isolado de **174 tabelas**, com **369 ficheiros** conferidos. Depois: **171 tabelas inalteradas**; diferenças somente em dossiers, auditoria e sessões. Biblioteca/documentos, 20 artigos públicos, 28 privados, geometrias e atribuições de papéis preservados. O observador do deploy foi retomado. A ponte de acesso antiga tinha processos locais concorrentes; foram encerrados os clientes desta tarefa e usada uma única ligação autenticada. Não se registaram credenciais.
**Conferência limitada da reposição, sem suite geral:** consulta anónima `/carteira` com 29 dossiers, projectos com 15, clusters com 8 e corredores com 6, todos HTTP 200. A antiga mensagem de carteira vazia fica superada por esta reposição. Links: [carteira](https://teste-investidor.tempo.co.mz/carteira), [projectos âncora](https://teste-investidor.tempo.co.mz/projectos-ancora), [clusters](https://teste-investidor.tempo.co.mz/clusters), [corredores](https://teste-investidor.tempo.co.mz/corredores). Recibo/inventário: [preservation-inventory-20261023.json](preservation-inventory-20261023.json); manifesto de proveniência: [approved-portfolio-visibility-20261023.json](approved-portfolio-visibility-20261023.json). Não se repetiram as provas funcionais aceites de outros módulos.
**Preservação nas próximas entregas:** usar este inventário e a instrução de 23/10 em `AGENTS.md` como referência anterior à alteração. Conferir o delta de código, schema, dados, configuração e permissões, explicando qualquer redução de disponibilidade. Uma reposição não apaga edições posteriores nem revoga aprovações antigas por omissão. Reversão deve preservar as novas versões e a auditoria; não substituir toda a base por um backup anterior. O conjunto parcial da matriz continua parcial no seu âmbito, sem retirar o que já foi entregue.
## Guia curto — entrada única de gestão
**Link da equipa:** https://teste-investidor.tempo.co.mz/gestao-conteudos. Entre com a conta habitual e o seu MFA. As funções e âmbitos aprovados determinam os botões apresentados; não é necessário acesso à linha de comandos.
| Trabalho | Percurso pela interface |
| --- | --- |
| Oportunidades | **Nova oportunidade** → escolher promotor real e modalidade → preencher → **Guardar rascunho**. Recuperar em **Gerir oportunidades e histórico**; abrir a ficha, completar **Imagens, documentos e localizações** e **Submeter para validação**. O Validador usa a fila de revisão e as acções **Iniciar validação**, **Guardar revisão especializada**, pedir correcções ou **Aprovar versão**, conforme a tarefa atribuída. O Publicador usa a fila de publicação e **Publicar no catálogo público** na versão aprovada com visibilidade pública. A equipa conclui as funções que já lhe foram atribuídas; a criação não concede revisão nem publicação. |
| Documentos | **Novo documento** → escolher ficheiro e origem conhecida → **Guardar em quarentena** → conferir a análise indicada no ecrã → completar **Registar documento na biblioteca** → **Guardar como rascunho**. Recuperar pela pesquisa em **Gerir conteúdos, versões e histórico**; usar **Editar metadados** para rever; guardar as alterações e usar **Publicar**. Se o scanner comunicar falha, usar **Tentar novamente**; avaria persistente é manutenção técnica, não uma nova aprovação documental. |
| Legislação | **Nova legislação** → mesmo percurso documental, com tipo Legislação pré-seleccionado → registar entidade, número, edição, fonte, datas e situação conhecida → guardar → pesquisar/reabrir → rever metadados/associações → **Publicar**. Fonte oficial identificada mantém a aceitação da Tempo; não exige segunda certificação. A publicação permite consulta e não activa regras fiscais. |
Para retirar um documento, abrir o registo e usar **Retirar da publicação**; original e histórico ficam preservados. Para oportunidades, usar as acções de suspensão/arquivo apresentadas à função responsável. Não se apagam registos para os retirar do público.
Este guia descreve os percursos existentes; a instalação de código é manutenção pelo processo Git automático. Contas ainda não criadas conservam as designações aprovadas e não suspendem os acessos das contas existentes. Conferência real desta instalação registada na secção de entrega, abaixo; o guia não substitui essa evidência.
## Parques — disponibilizados em18/10/2026
Revisão `parques-integral-reconciliado-639ed9624a48` disponibilizada em `https://portal-investidor.test`, com autorização expressa da Tempo. Sete fontes (cinco funcionais e dois auxiliares gerados) e170 ficheiros de build; nenhuma migration executada. L1/140000, navegação/150000 e editorial160000 preservados. Backup/restauro fresco e preservação de dados conferidos. Os dados reais desconhecidos mantêm-se desconhecidos; não se encerram todos os requisitos da ficha pelo nome integral.
@@ -115,11 +632,19 @@ Relatório desta observação: [verificação editorial](../../../outputs/invest
## Matriz executável
Decisão transversal da Tempo, 23/10/2026: percurso adaptativo com entradas por ideia/oportunidade/sector/localização; perguntar uma vez, preservar edições deliberadas e contexto entre ecrãs/autenticação; seleccionar opções únicas; ocultar exigências não aplicáveis sem confundir ausência de informação com dispensa; conservar especificidades na selecção múltipla. Mostrar posição, pendências e próxima acção. Relatórios/exportações seguem a mesma aplicabilidade. Origem: pedido expresso da Tempo nesta data; implementação incremental nos componentes existentes, sem novos aprovadores.
Incremento de apresentação: o dossier conserva as 19 etapas e o histórico, mas apresenta formulários apenas das etapas com preparação permitida. As restantes têm resumo do fundamento e podem regressar ao rever a decisão. A ferramenta Terra/DUAT deixa de ser proposta quando `land_needed=no`; `unknown` mantém a preparação disponível. Relatórios do plano reúnem requisitos documentais `not_applicable` numa explicação, mantendo requisitos em falta ou indeterminados. Esta alteração não remove obrigações jurídicas nem modifica dados guardados. A aplicação transversal a todos os formulários, entradas e exportações permanece por conferir; não se encerra esse requisito por documentação.
| Menu / ecrã | Componente React | Campos / entidade / tabela | Rota ou API | Perfil / regra | Evidência |
| ------------------------------ | ------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------- |
| Preparação adaptativa / relatório | `investor-path/show`, `investment-requirements`, `project-dossier-report` | Aplicabilidade e contexto existentes; histórico preservado | `/investimentos/{id}`, `/aplicabilidade`; exportações existentes do plano | Tempo, 23/10/2026: ocultar preparação sem aplicação; desconhecido não é dispensa | Incremento QA: `InvestmentCorridorContextTest::test_nonapplicable_land_hides_the_action_and_reopening_recovers_saved_evidence` (18 asserções); relatório 11 testes; types/build; cenário sintético no navegador. Cobertura transversal ainda parcial. |
| Dossier → simulador sem repetir dados | `dossier-prefill`, `simulation-form`, `context-inheritance` | Campos existentes, `context_reuse`, `selection_labels`; sem migration | `/investimentos/{id}/contexto`, `/simulador-fiscal?context={id}` | Resumos apenas para valores herdados intactos; alteração deliberada preservada; perfil/detalhe/regime não presumidos | Incremento QA de continuidade, 23/10/2026: 12 testes de reutilização e 14 asserções de nomes exactos; cobertura geral permanece parcial |
| Situação do terreno adaptada | `simulation-form`, `investment-land-preparation`, `project-dossier`; `SimulationFormDrafts` | `land_needed` existente; situação e rótulo conservados no rascunho cifrado | APIs existentes de contexto/rascunho e exportações do plano | Tempo: Não oculta, Sim recupera e permite editar, Por definir identifica pendência sem bloquear rascunho | Delta QA 23/10/2026: 4 testes frontend e 32 asserções API; sem migration; homologação não alterada |
| Início | `PortalInvestidor` | Sectores, oportunidades, recursos publicados | `/`, APIs existentes de homepage | Visitante; contagens do cadastro | `PublicPortalNavigationTest`, navegador |
| Catálogo / Educação | `investment/catalogue` | Filtros, educação, pivots, facetas | `/oportunidades`, `/educacao`; `/api/v1/investment/search` | Visitante; filtros em intersecção, montantes com moeda | `InvestmentUnificationTest` |
| Cluster / Corredor / Âncora | `portfolio/index`, `portfolio/show` | Dossiers, relações, cadeias e oportunidades | `/clusters`, `/corredores`, `/projectos-ancora`, `/carteira/{id}` | Visitante; dossier publicado; cadeia pública | `InvestmentPortfolioTest`, testes territoriais |
| Continuidade carteira → oportunidade → dossier → simulador | Componentes de carteira, `investment/detail`, contexto existente | Origem documental por oportunidade no payload cifrado; associações existentes, sem inferência | `from_dossier` e território no arranque; rascunho, prévia, confirmação e simulador existentes | Mesmo percurso integrado; origem relacionada/publicada; respostas deliberadas conservadas | Visibilidade: 29 IDs/títulos conferidos anteriormente. Delta QA: seis testes/151 asserções API, cinco testes de entrada territorial, um de relatório; instalação na homologação pendente |
| Ficha | `investment/detail` | Oportunidade, contexto, relações, fonte | `/oportunidades/{slug}`; `/api/v1/investment/opportunities/{slug}` | Visitante; rascunhos não acessíveis | `PublicPortalNavigationTest`, `InvestmentUnificationTest` |
| Mapa | Componente `Map` em catálogo | `geo_locations`, `geo_geometries`, `opportunity_locations` | `/api/v1/investment/map` | Visitante; dois níveis de classificação pública | Teste PostGIS e estados vazios |
| Comparar / exportar / sugerir | `investment/catalogue` | IDs publicados, dados de catálogo | `/compare`, `/export`, `/suggest` sob prefixo da API | Visitante; 4 comparações, 1.000 linhas/pontos; CSV protegido | Teste de publicação e fórmula CSV |
@@ -1006,53 +1531,53 @@ Reversão preparada em `REVERSAO-APRESENTACAO-INTEGRAL.json` e `reverse-authoriz
## Saldo dos 43 componentes
**12 disponíveis; 12 instalados mas desabilitados/por disponibilizar; 15 não demonstrados; 4 ausentes por serem incrementos QA excluídos.** “Disponível” tem o âmbito e limite indicado na linha; não certifica todos os critérios históricos do componente. Acesso aponta para o ecrã existente e não concede permissões: os acessos administrativos requerem as autorizações/MFA já existentes.
**Saldo histórico de 19/10/2026 — não representa o estado actual.** Conservado para rastreabilidade. A coluna final reconcilia as entregas posteriores e a leitura de 23/10, sem retirar aceitações por falta de nova prova. Disponibilidade, conclusão integral e ausência de dados são estados distintos.
| Nº | Componente | Estado | Acesso e limitação |
|---:|---|---|---|
| 1 | Documentos públicos | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/documentos) · 29 fichas privadas; zero publicação adicional. Consulta pública do acervo permanece fechada. |
| 2 | Ficha integral dos parques | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/zonas) · 16 fichas privadas/pendentes, 19 relações territoriais; publicação e gestão autorizada separadas. |
| 3 | Navegação contextual e manifestação de interesse | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/investimentos/iniciar) · Início, contexto e navegação abrem; gravação/recuperação aceites em QA reutilizadas. Sem dossier real no destino. |
| 4 | Referências económicas — lote mínimo | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/investment/references) · 11 actividades presentes; cinco novas referências transportadas. Activação/publicação do lote não executada. |
| 5 | Sectores e duplicação visual | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/sectores) · Ecrã aberto: dez sectores principais, especialização Fintech sem cartão principal duplicado; 12 registos conservados. |
| 6 | Duplicados distintos — deteção e consolidação | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/management/sector-reconciliation) · Código presente; consolidação de registos distintos não demonstrada no destino. |
| 7 | Fintech — enquadramento específico | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/sectores/mercado-financeiro/fintech) · Código instalado; artigo de origem permanece privado, sem publicação adicional. |
| 8 | Legado — conteúdo restante | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/sectores) · Adaptador habilitado; artigos/imagens transportados privadamente, sem os publicar. |
| 9 | Piloto agronegócio | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/sectores) · Originais privados; seis oportunidades sintéticas excluídas. Nenhuma oportunidade real publicada. |
| 10 | Percurso do investidor — 19 etapas | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/investimentos) · Início e lista própria abrem; definição de 19 etapas instalada. Não repetidas as 19 etapas aceites. |
| 11 | Fiscal/contributivo e exportações | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Formulário aberto; cenários fiscais/contributivos/exportação e parâmetros efectivos não certificados nesta execução. |
| 12 | Negócios existentes e classificação | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/negocios) · Catálogo abre e módulo habilitado; zero negócios copiados. Gestão própria sujeita às permissões existentes. |
| 13 | Negócios similares | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/negocios) · Código instalado; comparação com negócios reais não demonstrada. |
| 14 | Importação de empresas/empreendimentos | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/management/negocios) · Mecanismo presente; nenhuma empresa/transacção local ou QA transferida. Operador de gestão por definir. |
| 15 | Fundos | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; critérios específicos de fundos não demonstrados no destino. |
| 16 | Comparador societário e modelos | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; comparação societária/modelos não demonstrados nesta execução. |
| 17 | Capitalização/private equity e Bolsa | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; cenários específicos de capitalização/Bolsa não demonstrados no destino. |
| 18 | Checklists versionadas | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/servicos) · Definições presentes; checklist aplicada a dossier legítimo continua por demonstrar. |
| 19 | Indicadores de utilização | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/dashboard) · Código presente; indicadores com dados reais e permissões adequadas não demonstrados. |
| 20 | Jornadas educativas | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/educacao) · Área abre e flag habilitada; nenhum percurso educativo completo foi repetido. |
| 21 | Partilha documental individual e variantes de sala | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/parcerias/painel) · Estruturas de salas/partilha instaladas; participantes, consentimentos e partilha real não demonstrados. |
| 22 | Restauro, modalidades e SLA | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Backup/restauro coberto separadamente; modalidades/SLA não homologados. Escalonamento continua desligado como local. |
| 23 | Cartografia e consumidores territoriais restantes | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Catálogos/mapas abrem. 1 921 referências geográficas no alvo versus 1 990 locais; diferença não reconciliada por item fora do manifesto. |
| 24 | Acessibilidade e restantes consumidores | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Mensagens R2 preservadas; não se declara certificação integral de acessibilidade de todos os módulos. |
| 25 | Fechos de operação/avaliação documental | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Sem processo operacional legítimo para demonstrar fecho/avaliação documental. |
| 26 | Conta simples, login e MFA privilegiado | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/login) · Login e desafio MFA reais concluídos; conta simples R2 e controlos privilegiados preservados. |
| 27 | Manifestação simples, recibo e acompanhamento do interessado — R2 | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/investidor/manifestacoes) · Recorte R2 conservado, com provas aceites reutilizadas; nenhuma manifestação fictícia criada. |
| 28 | Pesquisa — estados do recorte R2 | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Catálogos abrem; quatro estados e protecção contra respostas antigas preservados do R2, sem repetir ensaios aceites. |
| 29 | Biblioteca privada própria, scanner e recuperação — DOC01 | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/management/portal-services) · Conta de custódia vê 30 ficheiros limpos. Novo original recuperado no navegador com SHA-256 igual à fonte; sem gestão global. |
| 30 | Backup/restauro e reversão do recorte R2 + DOC01 | Disponível | Backup fresco consistente: 144 tabelas restauradas iguais, 34 ficheiros/configuração conferidos, recuperação portável e metadados Linux/PG protegidos. Reversão de apresentação preparada. |
| 31 | Balcão Virtual | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Painel e serviço existente abrem; dez tabelas base instaladas, definição interna project-preparation v5 transportada. |
| 32 | Vínculo positivo e recuperação do dossier | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/investimentos) · Vínculo documental positivo pendente: zero dossiers da conta no destino. Não foram criados dados fictícios. |
| 33 | Cadastro canónico e recursos de oportunidade | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/opportunities) · Editor/schema instalados; zero oportunidades. Gestão/revisão exigem atribuições existentes ainda não configuradas. |
| 34 | Perfis, revisão independente e publicação | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/perfis/gestao) · Schema e fluxos presentes; zero atribuições/titulares. Nenhum papel ou privilégio concedido automaticamente. |
| 35 | Bolsa de parcerias e salas internas | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/parcerias) · Catálogo e mapa abrem, nove tabelas de relações/salas instaladas; colaboração com participantes reais não demonstrada. |
| 36 | Onde se localiza / associações territoriais e produtivas | Ausente | Harmonização territorial apenas QA, expressamente excluída da revisão autorizada. |
| 37 | Lote privado Inhambane —30 fichas | Ausente | 30 fichas Inhambane apenas QA, não importadas nem publicadas no destino. |
| 38 | Enquadramento das12 fichas de programas/carteira | Ausente | Enquadramento das 12 fichas permanece no incremento QA separado. |
| 39 | Originais partilhados e referência documental no dossier — Inhambane | Ausente | Reutilização documental Inhambane/dossier permanece no incremento QA separado. |
| 40 | Orientação assistida/IA | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/ai) · Código presente; integração desligada. Credencial própria do destino/chamada externa não confirmadas. |
| 41 | Integração externa SuiteCRM | Instalado mas desabilitado | Permanece desligada como na versão local; nenhuma credencial ou envio externo copiado/executado. |
| 42 | Operação administrativa e resposta da equipa na homologação | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/interests) · Nenhuma conta com gestão global/atribuições operacionais. Custódia documental não autoriza resposta/revisão da equipa. |
| 43 | Gestão editorial da biblioteca | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/portal-services) · Schema editorial instalado; publicação/edição pública não concedida à conta de custódia. |
| Nº | Componente | Estado histórico | Acesso e limitação históricos | Saldo de preservação em 23/10/2026 |
|---:|---|---|---|---|
| 1 | Documentos públicos | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/documentos) · 29 fichas privadas; zero publicação adicional. Consulta pública do acervo permanece fechada. | Disponível: 29 recursos públicos; recibo editorial 22/10. Os 20 artigos conservam a aceitação anterior. |
| 2 | Ficha integral dos parques | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/zonas) · 16 fichas privadas/pendentes, 19 relações territoriais; publicação e gestão autorizada separadas. | Disponibilizado localmente em 18/10; destino conserva 16 fichas, 8 publicadas. Recorte integral não é reaprovado por este inventário. |
| 3 | Navegação contextual e manifestação de interesse | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/investimentos/iniciar) · Início, contexto e navegação abrem; gravação/recuperação aceites em QA reutilizadas. Sem dossier real no destino. | Navegação/percurso preservados; carteira reposta em 23/10: 29 dossiers, incluindo 15 projectos âncora. |
| 4 | Referências económicas — lote mínimo | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/investment/references) · 11 actividades presentes; cinco novas referências transportadas. Activação/publicação do lote não executada. | Código e referências presentes; manter o âmbito da autorização anterior, sem inferir publicação de todas as opções. |
| 5 | Sectores e duplicação visual | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/sectores) · Ecrã aberto: dez sectores principais, especialização Fintech sem cartão principal duplicado; 12 registos conservados. | Entrega de 18/10 conservada no código/histórico; sem remoção constatada nesta leitura. |
| 6 | Duplicados distintos — deteção e consolidação | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/management/sector-reconciliation) · Código presente; consolidação de registos distintos não demonstrada no destino. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 7 | Fintech — enquadramento específico | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/sectores/mercado-financeiro/fintech) · Código instalado; artigo de origem permanece privado, sem publicação adicional. | Fintech público no lote aceite; especialização preservada. Não reabrir a organização sectorial aprovada. |
| 8 | Legado — conteúdo restante | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/sectores) · Adaptador habilitado; artigos/imagens transportados privadamente, sem os publicar. | 20 artigos públicos e 28 privados cadastrados no destino; dois originais incompletos. Recibo 37a8eb01. |
| 9 | Piloto agronegócio | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/sectores) · Originais privados; seis oportunidades sintéticas excluídas. Nenhuma oportunidade real publicada. | Piloto Agronegócio concluído: artigo e imagem preservados; não é uma oportunidade empresarial concreta. |
| 10 | Percurso do investidor — 19 etapas | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/investimentos) · Início e lista própria abrem; definição de 19 etapas instalada. Não repetidas as 19 etapas aceites. | Percurso e preparação privada habilitados no destino; entrega adaptativa de 22/10. Zero dossiers pessoais não é perda de função. |
| 11 | Fiscal/contributivo e exportações | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Formulário aberto; cenários fiscais/contributivos/exportação e parâmetros efectivos não certificados nesta execução. | Tesouraria/exportações e consumidor IVA têm entregas delimitadas em 21–22/10; restantes variantes não promovidas a concluídas. |
| 12 | Negócios existentes e classificação | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/negocios) · Catálogo abre e módulo habilitado; zero negócios copiados. Gestão própria sujeita às permissões existentes. | Módulo habilitado e fluxos conservados. Ausência de anúncios não demonstra desaparecimento de código. |
| 13 | Negócios similares | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/negocios) · Código instalado; comparação com negócios reais não demonstrada. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 14 | Importação de empresas/empreendimentos | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/management/negocios) · Mecanismo presente; nenhuma empresa/transacção local ou QA transferida. Operador de gestão por definir. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 15 | Fundos | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; critérios específicos de fundos não demonstrados no destino. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 16 | Comparador societário e modelos | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; comparação societária/modelos não demonstrados nesta execução. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 17 | Capitalização/private equity e Bolsa | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/simulador-fiscal) · Fontes instaladas; cenários específicos de capitalização/Bolsa não demonstrados no destino. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 18 | Checklists versionadas | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/servicos) · Definições presentes; checklist aplicada a dossier legítimo continua por demonstrar. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 19 | Indicadores de utilização | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/dashboard) · Código presente; indicadores com dados reais e permissões adequadas não demonstrados. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 20 | Jornadas educativas | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/educacao) · Área abre e flag habilitada; nenhum percurso educativo completo foi repetido. | Jornadas habilitadas; cartões educativos disponibilizados. Mantido o âmbito parcial da educação. |
| 21 | Partilha documental individual e variantes de sala | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/parcerias/painel) · Estruturas de salas/partilha instaladas; participantes, consentimentos e partilha real não demonstrados. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 22 | Restauro, modalidades e SLA | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Backup/restauro coberto separadamente; modalidades/SLA não homologados. Escalonamento continua desligado como local. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 23 | Cartografia e consumidores territoriais restantes | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Catálogos/mapas abrem. 1 921 referências geográficas no alvo versus 1 990 locais; diferença não reconciliada por item fora do manifesto. | Demarcação reposta: 408 geometrias. Polígonos de localidades e correspondências já pendentes não são nova regressão. |
| 24 | Acessibilidade e restantes consumidores | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Mensagens R2 preservadas; não se declara certificação integral de acessibilidade de todos os módulos. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 25 | Fechos de operação/avaliação documental | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Sem processo operacional legítimo para demonstrar fecho/avaliação documental. | Sem desaparecimento demonstrado; conserva o âmbito parcial/não demonstrado anterior, sem promover conclusão nem reabrir provas aceites. |
| 26 | Conta simples, login e MFA privilegiado | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/login) · Login e desafio MFA reais concluídos; conta simples R2 e controlos privilegiados preservados. | Login/MFA e designações reais documentados; sem alteração de papéis nesta passagem. |
| 27 | Manifestação simples, recibo e acompanhamento do interessado — R2 | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/investidor/manifestacoes) · Recorte R2 conservado, com provas aceites reutilizadas; nenhuma manifestação fictícia criada. | R2 e percursos posteriores conservados; reutilizar as provas aceites. |
| 28 | Pesquisa — estados do recorte R2 | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/oportunidades) · Catálogos abrem; quatro estados e protecção contra respostas antigas preservados do R2, sem repetir ensaios aceites. | Pesquisa e selectores corrigidos em 22/10; a carteira vazia é tratada como perda separada. |
| 29 | Biblioteca privada própria, scanner e recuperação — DOC01 | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/management/portal-services) · Conta de custódia vê 30 ficheiros limpos. Novo original recuperado no navegador com SHA-256 igual à fonte; sem gestão global. | Biblioteca, scan e recuperação disponíveis: 59 recursos, 29 públicos e 30 privados. |
| 30 | Backup/restauro e reversão do recorte R2 + DOC01 | Disponível | Backup fresco consistente: 144 tabelas restauradas iguais, 34 ficheiros/configuração conferidos, recuperação portável e metadados Linux/PG protegidos. Reversão de apresentação preparada. | Backup/restauro 22/10 documentado: 174 tabelas. Reposição activa terá backup próprio antes da escrita. |
| 31 | Balcão Virtual | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/balcao-virtual/painel) · Painel e serviço existente abrem; dez tabelas base instaladas, definição interna project-preparation v5 transportada. | Balcão e definições instalados; zero casos reais no destino não é perda de funcionalidade. |
| 32 | Vínculo positivo e recuperação do dossier | Não demonstrado | [Abrir](https://teste-investidor.tempo.co.mz/investimentos) · Vínculo documental positivo pendente: zero dossiers da conta no destino. Não foram criados dados fictícios. | Recuperação de contexto demonstrada em QA nas entregas posteriores; ausência de caso real não reabre a prova aceite. |
| 33 | Cadastro canónico e recursos de oportunidade | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/opportunities) · Editor/schema instalados; zero oportunidades. Gestão/revisão exigem atribuições existentes ainda não configuradas. | Cadastro e recursos disponibilizados; 30 oportunidades com resumos em preparação publicados. |
| 34 | Perfis, revisão independente e publicação | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/perfis/gestao) · Schema e fluxos presentes; zero atribuições/titulares. Nenhum papel ou privilégio concedido automaticamente. | Perfis e atribuições existentes preservados; inventário não cria decisão organizacional nova. |
| 35 | Bolsa de parcerias e salas internas | Disponível | [Abrir](https://teste-investidor.tempo.co.mz/parcerias) · Catálogo e mapa abrem, nove tabelas de relações/salas instaladas; colaboração com participantes reais não demonstrada. | Bolsa e percursos existentes preservados; variantes com participantes reais mantêm saldo próprio. |
| 36 | Onde se localiza / associações territoriais e produtivas | Ausente | Harmonização territorial apenas QA, expressamente excluída da revisão autorizada. | Estado Ausente superado: integração territorial entregue em 20–22/10; catálogos e 408 limites conservados. |
| 37 | Lote privado Inhambane —30 fichas | Ausente | 30 fichas Inhambane apenas QA, não importadas nem publicadas no destino. | Estado Ausente superado: 30 fichas Inhambane transportadas e resumos divulgados em 22/10. |
| 38 | Enquadramento das12 fichas de programas/carteira | Ausente | Enquadramento das 12 fichas permanece no incremento QA separado. | Estado Ausente superado: enquadramento de 11 programas e uma carteira incluído na entrega posterior. |
| 39 | Originais partilhados e referência documental no dossier — Inhambane | Ausente | Reutilização documental Inhambane/dossier permanece no incremento QA separado. | Estado Ausente superado: originais privados/referências e contexto incluídos na entrega posterior; não reimportar. |
| 40 | Orientação assistida/IA | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/ai) · Código presente; integração desligada. Credencial própria do destino/chamada externa não confirmadas. | Sem regressão demonstrada: integração externa não fechada; não inventar credencial nem activar fornecedor. |
| 41 | Integração externa SuiteCRM | Instalado mas desabilitado | Permanece desligada como na versão local; nenhuma credencial ou envio externo copiado/executado. | Desabilitada também no local por opção anterior; não desapareceu por efeito da entrega editorial. |
| 42 | Operação administrativa e resposta da equipa na homologação | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/interests) · Nenhuma conta com gestão global/atribuições operacionais. Custódia documental não autoriza resposta/revisão da equipa. | Estado antigo superado: conta real e gestão autorizada demonstradas nas entregas posteriores. Sem novos papéis. |
| 43 | Gestão editorial da biblioteca | Instalado mas desabilitado | [Abrir](https://teste-investidor.tempo.co.mz/management/portal-services) · Schema editorial instalado; publicação/edição pública não concedida à conta de custódia. | Gestão autónoma entregue; edição, prévia, gravação e recuperação dos 28 privados demonstradas no destino. |
## Operação corrente e pendências
@@ -2151,3 +2676,335 @@ Autorização expressa da Tempo executada. Main passou de `57fc9148` por avanço
**Estado fiscal preservado:** nenhuma publicação/activação de nova regra e nenhuma operação na base activa. O consumidor IVA geral previamente autorizado manteve a mesma disponibilidade, cobertura2026 e mensagem antes/depois. As exclusões sectoriais, forma indirecta/dimensão monetária não determinada, benefícios territoriais e apuramento fiscal completo continuam explícitos e não bloqueiam o lote procedimental concluído.
**Suspensão/recuperação:** desligar somente a chave do novo percurso e reconstruir cache/reiniciar graciosamente os workers mantém dossiers, documentos, respostas, versões, relatórios e históricos. Para reverter configuração, usar o valor anterior apenas depois de conferir o hash actual e preservar alterações posteriores; não substituir todo o .env sem conferir concorrência. A reversão de código/build segue o procedimento de releases já existente, sem restaurar dados antigos. A release anterior foi efectivamente conservada na primeira tentativa recusada. Recibo completo local em `outputs/investmoz-adaptive-path-delivery-20261022/delivery-receipt.json`; a presente actualização documental fica na branch de registo, sem novo push main ou nova disponibilização desnecessária.
## Revis?o das 30 fichas de Inhambane e cat?logo vazio ? 22/10/2026
Leitura remota autenticada, transac??o READ ONLY, base teste_investidor: 30 oportunidades em draft; zero publicadas em toda a base. As consultas independentes de Cabo Delgado e Inhambane e a consulta exacta Inhambane/Vilankulo devolveram zero sem falha de consulta. A alega??o de oportunidades publicadas de Cabo Delgado n?o se confirma neste destino; n?o inventar associa??es para preencher a lista.
As 30 localiza??es permanecem nos recursos privados opportunity_location_drafts, referidos no cadastro can?nico; os campos e v?nculos p?blicos ainda n?o s?o a projec??o dessas fichas. A inexist?ncia desses v?nculos p?blicos em rascunhos n?o prova perda da localiza??o. Na publica??o, projectar somente a abrang?ncia documentada e conferir a pesquisa territorial.
Decis?o Tempo nesta conversa: incompletude e autoriza??es necess?rias ? execu??o n?o s?o impedimentos autom?ticos ? divulga??o como oportunidade em prepara??o. Proposta: divulgar resumos das 30 fichas em prepara??o com fonte, edi??o, data-base e lacunas vis?veis; n?o declarar financiamento, terreno, licen?as, promotor autorizado ou execu??o confirmados. Valores hist?ricos, agregados e divergentes devem ser identificados sem somas indevidas. Nenhum impedimento concreto de divulga??o do resumo foi demonstrado nesta leitura. Isto n?o declara o conte?do privado integral public?vel: originais, contactos e evid?ncias continuam separados. N?o houve publica??o, altera??o de pap?is ou escrita remota.
Prepara??o por ficha: outputs/investmoz-territory-recovery-20261022/publication-review.json (30 identidades reais, localiza??o privada, lacunas e valores). Evid?ncia: read-catalogue-remote.py e remote-read.log no mesmo direct?rio. A tabela hist?rica de lacunas de prepara??o ? reutilizada, sem a converter em bloqueios legais. A prepara??o t?cnica do fluxo de publica??o ainda deve reconciliar essas condi??es com o contrato existente; n?o marcar publica??o como pronta ou executada.
O URL recebido ? do cat?logo/iniciar por territ?rio, n?o do dossier afectado. N?o permite ler o contexto inicial, a altera??o e a ?ltima ac??o desse processo. A falha da pr?via territorial e do t?rmino sem resposta continua aberta e ainda n?o foi reproduzida no dossier real; n?o encerrar pela disponibiliza??o anterior ou por esta leitura de cat?logo.
<!-- search-territory-recovery-20261022 -->
## Continuidade territorial da pesquisa — correcção em QA, 22/10/2026
Esclarecimento da Tempo: o percurso reportado foi navegação pública entre carteira e catálogo, sem clicar em iniciar/continuar ideia. Não existe evidência de alteração de um dossier pessoal neste caso. Retira-se a exigência de URL do dossier e de confirmação de impacto; a alteração é de filtros de pesquisa.
Causa verificada: ligações da carteira para oportunidades e zonas e ligações dos menus públicos usavam destinos sem província/distrito. O contexto transportado para detalhes por `catalogue_query` também não acompanhava as ligações cruzadas. No ecrã de zonas, os parâmetros de exportação/detalhe omitiam a hierarquia territorial.
Correcção: `searchTerritoryHref` transporta exclusivamente os quatro níveis territoriais na URL entre carteira, oportunidades, zonas e educação, incluindo os respectivos detalhes. Reutiliza o `catalogue_query` existente e preserva os filtros próprios do destino. Uma selecção explícita de outro ascendente não herda descendentes incompatíveis. Sem armazenamento global, mistura de contas ou alteração de dossiers. Menus, navegação relacionada, breadcrumbs e ligações cruzadas usam a mesma função. O selector partilhado mantém a limpeza dos descendentes; não há confirmação adicional para pesquisar.
Verificação dirigida: seis testes Node (incluindo o selector partilhado), TypeScript e build isolado. No navegador QA: carteira Cabo Delgado → selecção Inhambane/Vilankulo → pesquisar → catálogo com ambos os campos preenchidos; recarregamento e regresso preservam a selecção; zonas herdam os dois níveis; mudar para Cabo Delgado limpa Vilankulo. Contagem zero, filtros activos e mensagem de ausência coerentes na base QA consultada. Não foram repetidos cálculos ou exportadores fiscais nem publicados registos para produzir resultados artificiais.
Provas: `outputs/investmoz-territory-recovery-20261022/before-carteira.png` e `after-catalogue.png` no workspace de entrega; testes em `tests/frontend/search-territory.test.mjs` e `territory-path.test.mjs`; logs QA `outputs/territory-types.log` e `territory-build.log`. O router privado QA selecciona o build isolado; não integra a candidata.
Entrega limitada a branch/QA. A homologação não foi alterada por esta correcção. Publicação das 30 fichas de Inhambane permanece separada: revisão por ficha preparada, nenhuma publicação executada; ainda por reconciliar a preparação técnica da divulgação em preparação com o fluxo existente. A correcção da pesquisa não depende dessa publicação.
## Inhambane — divulgação dos 30 resumos executada, 22/10/2026
Decisão expressa da Tempo na conversa: «publicar» os 30 resumos como oportunidades em preparação; responsável confirmado `nala@tempo.co.mz`. A conta concluiu autenticação normal com MFA. A primeira ligação abriu indevidamente `/management/opportunities`, que exige administração geral; `/minhas-oportunidades` estava acessível e foi confirmado no navegador. A ligação SSH estava pronta; não foi necessário pedir outra autenticação.
Executada uma operação privada de dados, limitada às 30 identidades e aos hashes das versões autorizadas, sob a decisão de divulgação da Tempo e a sessão real de Nala. Não se atribuiu papel geral de Publicador, administração, revisões técnicas, aprovação jurídica ou autorização de execução. O fluxo genérico de aprovação de projectos completos permaneceu intacto. A publicação promocional tem finalidade própria registada em `workflow_context.publication.purpose=promotional_preparation`; não equivale à conclusão desse fluxo. Cada operação registou responsável, conteúdo anterior, hash do lote e finalidade no histórico restrito.
O executor preserva o cadastro canónico privado, proprietário e organização, originais e contactos. A projecção pública contém título, resumo, limitações, fonte/edição e indicação «Em preparação». Montantes não confirmados não são apresentados como investimento disponível; os dados técnicos/financeiros históricos são assinalados como tal. A conferência editorial retirou instruções internas dos resumos, usando as limitações da tabela aprovada na conversa.
Detectado e resolvido o vínculo territorial em falta: as localizações privadas mencionavam Vilankulos, mas só tinham província estruturada. O cadastro público confirma MUNICÍPIO DE VILANKULO → posto VILANKULO → distrito VILANKULO → Inhambane. Foram associados ao distrito apenas 15 registos cuja localização documental menciona expressamente Vilankulos, incluindo programas de abrangência múltipla. Não se inferiu implantação, coordenadas ou relação por proximidade. As restantes fichas conservam a abrangência documentada; não foram associadas indiscriminadamente ao distrito.
Verificação pública sem sessão da aplicação: **30 resultados em Inhambane, 15 em Vilankulo**, todos com indicação «Em preparação», limitações e sem `management_draft`/`workflow_context` na resposta pública. No navegador foram conferidos catálogo, contagem e a ficha do Hotel Boutique no Terreno INATUR, incluindo a ausência de confirmação da disponibilidade do terreno/concessão. Foram reutilizadas as provas anteriores; o ensaio novo em base QA isolada conferiu publicação, alteração editorial, repetição sem duplicados, preservação do cadastro privado, recusa de alteração concorrente e associação exacta das 15 referências. Todos os dados sintéticos foram revertidos por rollback em QA; nenhum actor QA foi transportado.
Backup fresco em formato PostgreSQL custom antes de cada revisão de dados, protegido fora da raiz pública; catálogo de restauro verificado e provas anteriores de restauro reutilizadas. A leitura final confirmou as mesmas 30 identidades, cadastro privado/proprietários preservados, zero novos papéis Publicador e administração geral de Nala mantida desligada. A reversão deve partir do backup e do `before_data` restrito, comparar a versão actual e restaurar apenas as projecções/vínculos afectados, preservando edições posteriores; não restaurar toda a base sobre alterações concorrentes. Uma suspensão deve retirar os resumos do público, mantendo cadastro, relações e histórico, como operação expressamente registada; não apagar as fichas.
Recibos no workspace de entrega: `outputs/investmoz-territory-recovery-20261022/public-verification.json`, `integrity-receipt.log`, `publication-final-result.log`, `published-vilankulo-results.png`; executores privados e manifestos no mesmo directório, arquivados fora da raiz pública no destino. Hash final do lote: `d0a1146a7113aabafc3c2d08c0bdc17d12fc8a51606ba9ff7e79b154556d6e08`.
Revisão efectivamente servida: `042ffbde44cac27704d6f8b7ec25b525a42e3575`. Nenhuma instalação Git, migration ou activação fiscal foi executada nesta publicação. A candidata `9ac623a4` de continuidade dos filtros permanece separada de `main` e pronta para revisão de integração. A divulgação das 30 fichas deixa de estar pendente; os estudos, financiamento e autorizações de execução continuam como limitações explícitas das oportunidades.
## Integração territorial e contacto comercial — 22/10/2026
Tempo autorizou integrar `9ac623a4` e pediu conferir a revisão servida. `57fc9148` é antepassado de `042ffbde`, e não uma revisão posterior: os hashes não têm ordem cronológica. A conferência remota comparou 685 ficheiros de aplicação/configuração com `042ffbde`, sem diferenças; o manifesto de build também coincide com o identificador publicado. O histórico de instalação confirma a passagem de `57fc9148` para `042ffbde`. Não se identificou reposição de código/build ou desaparecimento da entrega do simulador.
Integração Git por avanço directo até `1ec31856`, incluindo `9ac623a4` e recibos documentais. A primeira tentativa automática preservou a revisão anterior por falha Node: importação sem extensão em `catalogue-context.ts`. Corrigida a referência explícita `.ts`; passaram os dez testes dirigidos do módulo afectado e da continuidade territorial. Não se repetiram os percursos aceites nem a publicação de oportunidades.
Decisão expressa da Tempo: o rodapé deve apresentar **comercial@tempo.co.mz**, porque o contacto comercial com a instituição é feito por esse endereço. Actualizado o valor por omissão de `portal.institutional_contact_email`. Na homologação existia uma sobreposição explícita `PORTAL_INSTITUTIONAL_CONTACT_EMAIL=info@tempo.co.mz`: alterada exclusivamente essa chave para `comercial@tempo.co.mz`, com backup privado íntegro, propriedade/permissões preservadas e reconstrução da cache de configuração. A apresentação e o `mailto:` utilizam a mesma configuração existente; não houve envio de email.
Recibos de destino e integração: `outputs/investmoz-territory-recovery-20261022/revision-before-integration.log`, `footer-email-receipt.log` e recibo final de instalação no mesmo directório do workspace de entrega. A confirmação final deve identificar a revisão efectivamente instalada pelo processo automático, mantendo os 30 resumos e as regras fiscais existentes.
## Selectores especializados do catálogo — QA, 22/10/2026
Pedido da Tempo: explorar estruturas públicas independentemente da existência de oportunidades, sem inventar associações nem divulgar registos privados. A homologação em `7b1c3579` já devolve e apresenta **Parque Industrial de Balama** e **Parque Industrial de Montepuez** no selector de Cabo Delgado; “Todos” é o valor inicial, não uma prova de ausência das opções. A API devolve duas zonas nessa província e oito no âmbito nacional. A base QA local tem outro conjunto de dados: não continha parques públicos ligados a essa província. Esta diferença de dados não é uma regressão demonstrada do selector da homologação.
Falhas verificadas no código: actividades e cadeias eram reduzidas às oportunidades publicadas encontradas no território/sector; estruturas públicas podiam depender da existência adicional de uma ficha publicada da carteira; os selectores vazios só apresentavam “Todos”; a consulta da ficha de um parque sem oportunidades não era apresentada no resultado vazio da grelha.
Correcção na branch `codex/catalogue-specialised-selectors-QA`: opções provenientes dos cadastros públicos existentes, preservando os critérios de consulta e os vínculos territoriais. Estruturas com território principal documentado não dependem de uma oportunidade nem de uma ficha adicional da carteira. Mantêm-se os vínculos territoriais já resolvidos pelas fichas publicadas. Cadeias acompanham os clusters públicos do âmbito seleccionado. Actividades são o catálogo público de classificação, explicitamente separado da disponibilidade local de investimentos; não se inventa uma associação territorial ou sectorial. Os oito selectores apresentam contagem ou explicação de ausência. Uma selecção indisponível permanece identificada, com possibilidade de remoção, em vez de parecer “Todos”.
Quando a estrutura seleccionada não tem correspondências, o resultado informa “Sem oportunidades publicadas associadas com estes filtros” e mantém ligação à ficha pública existente, em qualquer vista. Um registo mestre sem ficha pública detalhada é identificado como tal; não se cria uma ficha ou publicação automaticamente. A pesquisa continua a combinar todos os filtros, sem alargamento automático ou atribuição de benefícios. A alteração territorial mantém a limpeza dos níveis e selecções incompatíveis já implementada.
Provas novas, sem repetir os percursos anteriormente aceites: Cabo Delgado com dois parques sintéticos identificados; selecção de Balama, zero oportunidades, abertura da ficha, regresso e recarregamento com selecção conservada; limpeza; Nampula com parque sintético Topuito; ausência de restrição territorial com ambos os conjuntos disponíveis e selecção anterior limpa. Fixtures limitadas à base `simulator_fiscal_delta_20261021_test`, porta 55441, excluídas do Git e da disponibilização. Testes `CatalogueSelectorReferencesTest`: três testes, 27 verificações, transacções revertidas, incluindo exclusão de privados/outro território, catálogo de actividades independente dos resultados, estruturas/cadeia sem oportunidades ou fichas adicionais. `npm run types:check` e build QA passaram. Provas locais: `outputs/catalogue-selectors-20261022/audit.json`, `qa-balama.png`, `qa-nampula.png`, `qa-national.png` e logs de build/tipos no mesmo directório.
Diagnóstico separado do mapa: API pública de Cabo Delgado devolve território cadastrado, zero limites e nenhuma geometria acessível. Leitura SSH em transacção **READ ONLY** confirmou zero linhas em `geo_geometries` para essa província; neste caso não é uma camada existente ocultada pelo perfil. Não foram criadas coordenadas, geometrias, vínculos ou permissões. Este diagnóstico não equivale à implementação dos limites administrativos.
Integração pendente da revisão prevista: comparar com `origin/main`, preservar alterações concorrentes e integrar pelo processo Git automático existente. Não há migrations, publicação de oportunidades, activação fiscal ou transporte das fixtures. Conferir apenas o delta e a revisão servida depois de autorizada a integração; não repetir as provas já aceites.
### Revisão dirigida da interacção do selector — 22/10/2026
Na candidata `1d921c9d`, ensaio pelo navegador Edge em QA, sem usar `selectOption` como prova desta interacção: clique no campo/seta deu foco; teclado `Alt+Down`, seta para cima e Enter mudou a selecção de Montepuez para Balama, actualizando o filtro e a ligação à ficha. Prova local `outputs/catalogue-selectors-20261022/qa-keyboard-balama.png`. Dados sintéticos explicitamente identificados como QA.
A ferramenta não observou o menu nativo aberto pelo rato; a tentativa de clicar na opção nativa devolveu opção não visível. Assim, a escolha completa **apenas pelo rato permanece não demonstrada** nesta revisão. Trata-se de limitação de observação/interacção da ferramenta, sem evidência suficiente para atribuir falha ao portal. A presença das opções na API/DOM e o uso anterior de `selectOption` não encerram esse ponto. Sem alteração funcional, repetição dos restantes testes ou integração em `main`; geometrias continuam como pendência distinta.
### Decisão de integração — 22/10/2026
A Tempo encerrou a revisão de `1d921c9d` com as provas existentes e autorizou integrar a candidata e o registo documental `916819eb`. A interacção exclusivamente pelo rato permanece não observada pela ferramenta, **sem carácter bloqueante**; não constitui falha comprovada do portal, não exige substituir o selector nem repetir ensaios. A ausência de geometrias administrativas de Cabo Delgado permanece independente. Executar o processo Git automático existente, preservando alterações concorrentes; não transportar fixtures QA, publicar dados ou activar regras fiscais.
## Entrada única de gestão — preparação em QA, 22/10/2026
Origem: prioridade expressa da Tempo para a equipa cadastrar oportunidades e carregar, editar e publicar documentos e legislação pela interface, sem depender do Codex. A decisão de 22/10 sobre fontes e execução mantém-se. Geometrias administrativas ficam no saldo separado.
Implementada `/gestao-conteudos`, acessível pelo painel, menu lateral, Balcão e editores. Reutiliza os cadastros, biblioteca, armazenamento, perfis e histórico existentes. Nova oportunidade abre o editor operacional para quem tem contexto de criação; acesso legado é identificado como tal, sem o apresentar como publicação operacional completa. A entrada não atribui papéis. Escolhas técnicas deste delta: navegação agregada, contagens no âmbito do utilizador, pesquisa documental e pré-selecção do ficheiro acabado de carregar. Não são novas regras organizacionais.
Percurso diário: **Gestão de conteúdos e oportunidades → Nova oportunidade**, escolher promotor/modalidade, preencher e guardar; recuperar em **Gerir oportunidades e histórico**. Para documentos: **Novo documento** ou **Nova legislação → Receber documento em privado → Guardar em quarentena → Registar documento na biblioteca**. O original recém-carregado fica seleccionado; título e emitente conhecidos são recuperados. Completar os metadados, guardar, pesquisar o registo e usar **Editar metadados**, **Publicar** ou **Retirar da publicação**. Associações apresentam nomes dos registos existentes. Campos ainda não conhecidos não são inventados. Publicação para consulta não activa cálculos.
Verificação nova: quatro testes, 99 asserções, com transacções revertidas exclusivamente em `simulator_fiscal_delta_20261021_test`, loopback 55441, guarda explícita. Demonstrados acesso de equipa sem administração geral, recusa a utilizador sem autorização e ciclo documental completo (upload, ClamAV real, recuperação por pesquisa, edição, publicação/download, retirada, preservação do hash e quatro revisões). Exemplo documental exclusivamente sintético, sem origem oficial ou valor jurídico. Corrigida a classificação de retirados: o histórico existente usa `unpublished`; o filtro e o rótulo reconhecem também `withdrawn` histórico. TypeScript e build QA passaram.
No navegador, login e MFA normais da conta sintética de equipa; entrada → legislação; entrada → nova oportunidade → guardar preparação incompleta → recarregar, com título recuperado e ficha QA 241. Não foi repetido o workflow integral de revisão/publicação de oportunidades já existente; a prova nova cobre a entrada e a gravação/recuperação. O ensaio documental completo foi HTTP transaccional, não uma prova visual de cada clique. A conta QA tem autorizações explicitamente sintéticas; não comprova as designações das contas reais. Não transportar fixtures, configurações QA ou concessões de acesso.
Provas locais: `outputs/content-management-20261022/entrada-final.png`, `build-final.log`, e `tests/Feature/ContentManagementEntryTest.php`. Estado: candidata em branch `codex/content-management-entry-QA`, ainda sem integração ou instalação na homologação. Não declarar encerrada a autonomia operacional de todas as contas/categorias apenas por esta entrada. Não houve migration, publicação remota, alteração de papéis activos ou activação fiscal.
## Recuperação da demarcação territorial — QA, 22/10/2026
Retomada expressa da Tempo como recuperação de funcionalidade anteriormente demonstrada, nos requisitos existentes AUD-GEO-02/03 e na experiência de cadastro. Não é criação de outra cartografia. O incremento de divulgação promocional permanece na sua branch independente.
**Causa comprovada:** leitura SSH autenticada, em transacção READ ONLY, da release `8286d750`/base `teste_investidor`: zero linhas em `geo_geometries`, incluindo eliminadas. Cadastro preservado: 11 províncias, 148 distritos, 437 postos e 1324 localidades. Directório dos originais privados ausente; importador desabilitado. Serviço de consulta, controlador e correspondências iguais por SHA256 ao código QA. Hash do cadastro (IDs, nomes, pais, nível, origem/códigos): `9de4181c42055bc6ef11b56333b64dd463d0ebe16bb2d1ea3238fc79e69024f7`, igual no QA e destino. Não se confirmou recusa de acesso nem perda de associação: faltam os próprios dados.
**Histórico:** as provas de 09–10/10 em `docs/investmoz/v2/auditoria-portal/correspondencias-cartograficas.md` registam 361→393→408 correspondências em bases QA anteriores. Os originais e recibos privados permanecem disponíveis. Os transportes de 19–20/10 deste manual excluíram expressamente geometrias; a disponibilização Git também exclui dados e ficheiros privados (`deploy/homologacao/README.md`). Em 22/10 a leitura de Cabo Delgado já encontrara zero linhas; esta passagem conferiu a tabela inteira. Prova-se uma diferença de composição entre QA cartográfico e homologação. Não foi localizado recibo da instalação anterior destes limites na mesma base; não se atribui data a uma suposta eliminação nem se descarta a observação anterior no mesmo endereço.
**Recuperação executada em QA:** o clone actual também tinha zero geometrias. Reutilizados seis originais INE/UNOCHA de 07/06/2019 (três GeoJSON e três metadados), hashes preservados e correspondências v2 existentes. O serviço `TerritoryGeometryImport` inseriu 11 províncias, 133 distritos e 264 postos, internos, com recibos e auditoria. Todos válidos/SRID4326. Nos exemplos, a consulta pública continua sem devolver limites internos. ClamAV real, assinaturas de 22/10: três originais limpos. ADM3, 26773796 bytes, excedeu o stream de 25 MiB e passou pelo scanner de ficheiro GIS com orçamento de 40 MiB nesse processo; tentativa inconclusiva preservada. Sem alterar limites globais ou configuração do daemon.
O mapa enquadra o limite seleccionado; se não existir, enquadra o nível superior disponível mais próximo e informa que é contexto. A lista distingue selecção de contexto superior. Fonte, edição e actualidade jurídica indeterminada preservadas. Limite administrativo não representa parcela nem autorização de investimento.
**Prova de interface:** oportunidade sintética QA268 criada no portal. Cabo Delgado → ANCUABE → posto ANCUABE → ANCUABE-SEDE; limites e aproximação progressiva nos três primeiros níveis. Recurso de contexto/principal guardado; após recarregar e abrir “Ver / editar / restaurar”, os quatro níveis foram recuperados. A localidade sem limite próprio apresenta aviso dos níveis superiores. A fixture QA255 anterior tinha versão fechada/payload sintético mínimo incompatível com gravação do contrato; não foi usada como prova de persistência nem corrigida silenciosamente. Uma navegação durante a recompilação QA conservava referência a asset removido; recarregar resolveu o carregamento, sem alteração no destino.
**Cobertura:** 408/581 elementos do exemplar aceite: 11/11 províncias, 133/159 distritos, 264/411 postos. Mantêm-se 26+147 correspondências pendentes, com nomes nas prévias e no documento de correspondências existente. O denominador do cadastro é diferente (148 distritos, 437 postos). Este exemplar não contém geometrias próprias de localidades ou parcelas; não atribuir limites superiores às 1324 localidades.
**Reposição preparada, não aplicada na homologação:** `outputs/investmoz-territory-recovery-20261022/demarcation/retained-geometry-restoration.zip`, SHA256 `53a5d8aa2be57603c928279cbd14561741dd2ec4b44b138fbc785380de6f951d`, contém seis originais e manifesto. Não contém dump de substituição, actores QA ou limites sintéticos. Procedimento existente:
1. Rever e integrar pelo Git o ajuste visual; conferir revisão efectivamente servida.
2. Colocar originais no armazenamento privado partilhado, conservando hashes. Executar scanner actual ligado aos originais. Conferir backup/restauro e prova de activação da revisão/alvo, como exige o importador já existente; disponibilizar a configuração do importador.
3. Gestor já autorizado, com MFA, abre `/management/territory-geometry-import`: prévia e aplicação de províncias, depois distritos, depois postos, com motivo/confirmar. Reconciliar prévia fresca no destino; não executar hashes antigos do QA. Recibos e reversão dos lotes novos existentes preservados.
4. Conferir a cadeia territorial no formulário do destino e a recuperação após guardar, mantendo lacunas explícitas.
O staging dos originais, backup/restauro e configuração inicial ainda exigem operação técnica. Depois da reposição, seleccionar, guardar e recuperar a localização/mapa usam a interface. Esta passagem só leu o servidor remoto: sem alterar dados, permissões, camadas ou configuração. Não declarar a reposição no destino concluída.
Origem das restrições: decisões expressas da Tempo e requisitos anteriores do importador para preservação/backup e autenticação. Enquadramento e mensagens são escolhas de implementação, sem novo papel ou aprovador. Fonte oficial aceite reutilizada sem exigir nova validação da credibilidade.
Provas na pasta desta recuperação: inventário dos seis ficheiros, prévias/recibos por nível, `verification.json`, `homologation-readonly.json` e capturas. TypeScript, build QA e sintaxe PHP verificados; provas editoriais anteriores não repetidas.
### Ajuste de acesso encontrado na disponibilização — 22/10/2026
A conta real de Superutilizador encontrou HTTP403 no importador: o middleware não incluía a cartografia no âmbito administrativo já aprovado e o serviço exigia exclusivamente o gate legado `manage-portal`. Corrigidos ambos para reutilizar `ScopedPortalAdministration`, com MFA de sessão e as exclusões existentes de Auditor. Não foram alteradas atribuições, papéis, contas ou o gate global. Origem do bloqueio: escolha antiga de implementação, não decisão organizacional da Tempo. Três testes transaccionais novos/15 asserções em QA: Superutilizador com MFA chega à validação de fonte/aplicação; sem MFA e conta comum são recusados; nenhum grant legado criado. Provas QA cartográficas aceites reutilizadas sem repetição.
## Demarcação territorial reposta na homologação — 22/10/2026
Pedido expresso da Tempo executado. Candidata `15d82611` revista e integrada em `73c046e1`; ajuste de acesso `95d4cf44`, testado em QA, integrado em `30aed7cd`. A revisão `30aed7cd12ef65f797191467232087643c396751` foi instalada pelo processo Git existente e confirmada em `/build/revision.json?demarcation=30aed7cd`. O transporte de dados foi executado separadamente do Git.
### Dados, backup e importação
Seis originais INE/UNOCHA de 07/06/2019 transportados para o armazenamento privado partilhado: hashes iguais ao manifesto aceite, ficheiros 0640 e directório 0750, fora da árvore pública. Análise real dos seis pelo ClamAV 1.4.3, assinaturas de 22/10/2026, resultado limpo. Utilizado o protocolo local FILDES do daemon existente, sem instalar scanner, alargar permissões dos originais ou alterar limites globais: [documentação oficial ClamAV](https://docs.clamav.net/manual/Usage/ClamdProtocol.html).
Backup completo de `teste_investidor`, snapshot exportado consistente, restaurado em `127.0.0.1:55441/demarcation_hml_restore_20261022_complete_test`. As 171 tabelas coincidem integralmente por contagem e checksum canónico; arquivo dos 333 ficheiros verificado por conteúdo/hash. Recibo remoto em `/var/backups/investmoz-demarcation-20261022-complete/backup-receipt.json`, dump SHA256 `f80551de827e954a35b66ee4c8f2fcc559a4c88b48b3dc799c33b74907079c9b`. A comparação inicial revelou diferenças de ordenação Linux/Windows e a visão RLS reduzida da conta da aplicação; a comparação final usou ordenação canónica e a mesma visão completa do backup, preservando também os registos fiscais. Não se desactivou RLS.
A conta real `nala@tempo.co.mz` (ID15), com login e MFA normais renovados pelo titular, abriu as prévias frescas e aplicou os três níveis pela interface `/management/territory-geometry-import`:
| Nível | Criados | Pendentes | Lote auditado |
| --- | ---: | ---: | --- |
| Províncias | 11 | 0 | ec2c7c27-e4a3-466e-91a4-60aba939d3a0 |
| Distritos | 133 | 26 | 142667c7-7309-4461-8d0a-fd00cb421577 |
| Postos administrativos | 264 | 147 | b6d5a7e2-2f44-4bd6-9634-d97ee58ab718 |
Total: **408 limites internos**, válidos, não vazios, SRID4326; três recibos e três eventos `geometry_import_applied` ligados ao actor real. As geometrias foram repostas para consulta autorizada no cadastro; não foram publicadas camadas internas. Reversão existente disponível por lote, preservando originais, histórico e reutilizações e recusando alterações posteriores. Não se executou reversão no destino.
### Delta e acessos conferidos
Na oportunidade real #30, «Centro de Emergência Turística e Medicina de Mergulho de Tofo», a sessão real abriu Recursos → Ver recurso. O mapa desenha o limite da **província de Inhambane**, com fonte/edição e identificação de território seleccionado. Não é um polígono próprio de Tofo: distrito/posto/localidade continuam por indicar nessa ficha, como antes. A oportunidade, o recurso e a publicação não foram gravados nem alterados. Captura: `hml-demarcacao-tofo.png`. A prova QA aceite de guardar/reabrir quatro níveis foi reutilizada, sem repetição.
Consultas de controlo no destino recuperaram 1/2/3 limites na cadeia Cabo Delgado → distrito Ancuabe → posto Ancuabe. A localidade Ancuabe-Sede continua sem limite próprio e com três limites ancestrais de contexto. O cadastro territorial conserva exactamente o hash anterior `9de4181c42055bc6ef11b56333b64dd463d0ebe16bb2d1ea3238fc79e69024f7`.
Conferidos os acessos das contas existentes: IDs15 e16 mantêm a designação aprovada de Superutilizador; ID15 mantém Promotor e Validador. Não foram criadas contas ou atribuições. O endpoint do cadastro aceita os perfis já autorizados; a demonstração visual autenticada foi com ID15. A conferência das atribuições de ID16 foi de política, sem simular outro login. Quinze contas sem o perfil correspondente não satisfazem essa autorização. Pedido HTTP sem sessão ao endpoint privado devolveu 401; API pública devolveu zero limites internos; tentativa de obter o original pelo caminho público devolveu 403. Originais privados e regras de acesso preservados. Contas ainda não criadas não bloquearam a reposição.
O importador tinha um bloqueio legado de implementação: exigia `manage-portal` e não reconhecia a administração já aprovada. Corrigido por reutilização de `ScopedPortalAdministration`, com MFA de sessão e exclusões de Auditor existentes. Três testes novos/15 asserções passaram; não foi criado novo papel, aprovador ou gate global. Não houve repetição das provas QA cartográficas aceites.
Após a importação, **166/171 tabelas integralmente iguais** ao backup. Apenas `geo_geometries` (0→408), `geo_write_receipts` (0→3), `audit_events` (+6), `cache` e `sessions` diferem. Oportunidades, documentos, cadastro territorial, atribuições e par de Superutilizadores preservados integralmente.
### Guia curto e estado final
Entrar em [Gestão de conteúdos e oportunidades](https://teste-investidor.tempo.co.mz/gestao-conteudos) → Gerir oportunidades e histórico → abrir a ficha → Imagens, documentos e localizações → abrir ou preparar a localização. Seleccionar a hierarquia documentada; o mapa enquadra o limite disponível. Se faltar geometria própria, a mensagem distingue o contexto superior. Guardar o recurso e reabrir pela mesma lista recupera a preparação; não é necessário comando ou Codex para a consulta ou cadastro corrente.
**Reposição concluída neste recorte.** Mantidas as 26 correspondências de distritos e 147 de postos pendentes, com nomes nos documentos/prévias existentes. O exemplar não contém polígonos próprios das localidades ou parcelas. Essas lacunas não bloqueiam os 408 limites recuperados. A data de eventual desaparecimento anterior no mesmo endereço continua não determinada. A divulgação promocional mantém o seu incremento separado; este fecho não a declara integrada.
Provas: `homologation-restored-readonly.json`, `staging-receipt.json`, `preservation-receipt.json`, `http-access-verification.json`, captura e recibos locais na pasta desta recuperação; backup/restauro e prova de activação em caminhos privados. Uma interrupção do transporte local foi resolvida por escrita atómica da mensagem, antes de qualquer importação. Não ficaram passos técnicos pendentes para esta reposição; futuras importações usam as salvaguardas de backup/MFA já existentes.
## Concursos e anúncios — implementação em QA, 22/10/2026
Origem: pedido expresso da Tempo no anexo «O InvesteMoz deve ter uma secção pública Concursos e anúncios», com continuação autónoma autorizada. Aplicam-se as decisões permanentes da Tempo de 12/10 e 22/10/2026. Este incremento trata a divulgação de procedimentos; não abre recepção electrónica de propostas nem altera os investimentos associados. A estimativa apresentada antes da implementação foi de 16–24 horas; não representa tempo efectivamente consumido.
Reutilizados: entrada `/gestao-conteudos`, autenticação/MFA, autorização editorial e documental, entidades, oportunidades, projectos, sectores, território, biblioteca, originais privados, análise ClamAV, download verificado e `audit_events`. Acrescentados apenas três cadastros: procedimentos com rascunho, versões/actos imutáveis e acompanhamento individual. Uma oportunidade/projecto pode ter vários procedimentos; cada um admite lotes e actos associados. Não foram criados papéis, designações de funcionários ou aprovadores. Contas ainda não criadas não bloqueiam as contas já autorizadas.
### Fontes, âmbito e escolhas de implementação
- A UFSA identifica o Decreto n.º 79/2022 na [página oficial do regulamento](https://www.ufsa.gov.mz/divnovoreg.php). O [quadro oficial do Banco Mundial](https://www.worldbank.org/ext/en/what-we-do/project-procurement/framework) disponibiliza regulamentos e documentos normalizados, incluindo a edição de Setembro/2025. Cada procedimento regista a sua edição e acordo aplicáveis; a edição mais recente não é imposta a acordos anteriores.
- Inventário deste recorte: instrução anexada, páginas oficiais referidas, pesquisa dos ficheiros acessíveis do repositório e referências da biblioteca existentes. Não foi identificado neste recorte um modelo oficial de concurso já fornecido como original local utilizável. Não se declarou importado ou analisado o articulado integral/modelos que não foram obtidos. Os documentos de demonstração são explicitamente sintéticos, sem classificação de autenticidade oficial. Os modelos disponibilizados pela Tempo usam a biblioteca existente, sem segunda validação genérica da credibilidade de fontes oficiais aceites.
- Decisão expressa da Tempo: separar regimes Estado/externo/privado/outro; não generalizar UGEA, Cadastro Único, garantia ou abertura pública; guardar preparação incompleta; distinguir publicação de origem e publicação do portal; preservar actos, privacidade e auditoria.
- Escolhas técnicas: só a prévia de publicação exige identificação, entidade, objecto, modalidade, instrumento/edição/fonte, calendário coerente, canal e motivo público. Rascunhos permanecem incompletos. Prazos mínimos são opcionais, citados por fonte/edição/modalidade; dias úteis usam exclusivamente o calendário documentado no registo. Não foi parametrizada uma tabela universal de mínimos legais nem presumido um calendário de feriados.
- Escolhas técnicas de preservação: concorrência por versão, prévia assinada pelo servidor, nova verificação dos anexos na publicação, documentos por ID/hash e histórico imutável. Retirar ou substituir um binário na biblioteca torna indisponível o anexo antigo, sem apontar silenciosamente para outros bytes. Contacto sem autorização, notas internas e anexos privados não integram o conteúdo público. Ligações a oportunidades/projectos privados não divulgam os seus títulos ou fichas.
- Autorizações reutilizadas: edição/publicação por contas já elegíveis para gestão documental completa ou edição editorial; auditor e conta simples não obtêm escrita. Acompanhamento exige apenas autenticação, incluindo conta sem email verificado. Estas escolhas não atribuem novos papéis e não dispensam os requisitos existentes de cada autorização.
### Guia curto da equipa
**Entrada QA:** [Gestão de conteúdos e oportunidades](http://127.0.0.1:18580/gestao-conteudos). Abrir **Novo concurso/anúncio**; escolher a entidade já cadastrada e, quando aplicável, oportunidade/projecto, sector e território. Indicar o regime e a edição do instrumento aplicável. Guardar mesmo com preparação incompleta; recuperar em **Gerir concursos e anúncios**. O motivo interno fica no histórico de gestão.
1. Preencher objecto, lotes, participação, financiamento, calendário, fuso horário e canal efectivo de apresentação. A data de origem é a referência do procedimento; o portal regista separadamente a sua publicação.
2. Usar **Carregar / gerir documentos e scan na biblioteca**, que abre outra página sem perder o editor. Carregar o original, acompanhar o scan, editar os metadados e disponibilizar explicitamente os documentos autorizados. Voltar ao concurso, usar **Actualizar op??es da biblioteca** e seleccionar os anexos. Marcar públicos só os destinados à divulgação; os restantes permanecem privados.
3. Guardar, escolher **Anúncio inicial** e **Preparar prévia pública**. Rever contacto, documentos, objecto, entidade, prazo e canal. O motivo público aparece também no histórico público. Usar **Publicar este acto**; alterações posteriores no rascunho não modificam essa publicação.
4. Para esclarecer ou rectificar, guardar a revisão e publicar o acto correspondente. Para prorrogar, indicar o novo prazo, guardar e escolher **Prorrogação**; a prévia mostra o anterior e o novo. Resultados/adjudicação ou cancelamento usam o mesmo percurso, com texto e motivo documentados. O fim do prazo muda a indicação para **Prazo de propostas terminado**, sem adjudicação automática. Actos informativos posteriores não reabrem um cancelamento/resultado.
5. Consultar o [catálogo público QA](http://127.0.0.1:18580/concursos); pesquisar por palavras, entidade, sector, localização, modalidade, financiador, regime, estado ou prazo. Abrir a ficha e **Voltar aos resultados** conserva os filtros. **Relatório imprimível → Imprimir anúncio** permite imprimir/guardar pelo navegador, incluindo o histórico dos actos.
6. Uma conta simples usa **Acompanhar concurso** e **Os concursos que acompanho**; a lista identifica novos actos desde o registo de acompanhamento. **Deixar de acompanhar** remove só a subscrição individual. Não há promessa de alerta por email nem entrega de proposta: apresentar a proposta no canal indicado no anúncio/documentos.
Publicação documental disponibiliza um original da biblioteca; publicação de oportunidade disponibiliza a ficha pelas etapas existentes; divulgação promocional divulga o resumo autorizado de preparação; publicação de concurso divulga um procedimento e os seus actos. Nenhuma dessas operações autoriza execução do investimento, activa cálculo fiscal ou comprova envio à UFSA/financiador.
### Evidência e limites de entrega
Base exclusivamente QA `simulator_fiscal_delta_20261021_test`, porta 55441, com guarda de identidade antes de migração/fixtures/testes. Testes transaccionais preservam a base existente. `ProcurementTest` cobre preparação incompleta, concorrência, três regimes, filtros e retorno, prévia desactualizada, publicação anónima, prorrogação, expiração sem adjudicação, resultados, cancelamento persistente, conta simples, recusas de escrita, scan real, anexo privado, download anónimo e retirada documental. Ver resultado final em `outputs/procurement-20261022/tests.log`. Navegação existente: seis testes aprovados; compilação QA e TypeScript aprovados.
Exemplos persistentes, todos **SINTÉTICO QA**, sem concursos reais: estatal `17f44d8d-d077-4dca-9a63-5eb1c85a6926` (inicial + prorrogação, prazo de 20/11 15h para 16/12 17h, Africa/Maputo); externo `e85861cd-ffbb-4348-9cce-d588a6e09b86` (aberto); privado `fe271b02-0bfd-4fe2-8db2-856567b5111f` (inicial + cancelamento). Os rascunhos foram preparados por fixture identificada; guardar/reabrir, prévias e publicações destes actos foram executados no navegador pela conta sintética existente, não por comando de publicação. Resultado/adjudicação e expiração foram demonstrados pelos testes HTTP, não por adjudicação de um registo real.
Consulta anónima confirmada depois de terminar sessão: três regimes/estados no catálogo, ficha prorrogada, filtros preservados, relatório e download real do anexo 38. Anexo 39 e contacto sem autorização ficam privados. Acompanhamento confirmado pela conta simples já existente, sem MFA ou papel editorial. Evidências locais: `management.png`, `catalogue.png`, `print-notice.png`, `watch.png`, `fixtures.json` e resultados em `outputs/procurement-20261022/`. Estes artefactos QA não contêm credenciais na entrega Git.
Operações correntes deste incremento são feitas pela interface: cadastrar, guardar, recuperar, carregar/editar documentos pela biblioteca, pré-visualizar, publicar, actualizar/prorrogar, publicar resultado/cancelamento, consultar/imprimir e acompanhar. Não requerem executor privado ou Codex por concurso. **Pendente técnico de disponibilização fora de QA:** revisão e integração Git, migração pelo mecanismo de entrega e activação de `PROCUREMENT_ENABLED` no destino autorizado. O código usa flag desligada por defeito. Nenhum anúncio real foi publicado, nenhuma base activa foi migrada e não há integração automática com UFSA/financiadores. Geometrias não foram alteradas.
### Proposta de instalação compatível com deploy automático — candidata `1bcf34c0`
**Estado: proposta para revisão da Tempo, sem execução no destino.** O guia e os links QA da secção anterior foram conferidos pelo executor; a Tempo informou que ainda não os conferiu. A evidência QA aceite não equivale a aceitação pela Tempo nem a disponibilização na homologação. Destino previsto: `https://teste-investidor.tempo.co.mz`; confirmar a configuração instalada antes de executar. Esta passagem não integra em `main`, não altera configuração remota e não publica anúncios.
**Dependência de entrega encontrada.** Em `deploy/homologacao/pipeline.py`, `source_contract()` calcula SHA-256 dos ficheiros de migrations com normalização CRLF→LF; `build()` compara o conjunto completo com `schema_contract` e recusa qualquer diferença antes do build. O pipeline não executa migrations de produção nem importa conteúdos. A promoção troca atomicamente a release, verifica a revisão servida e a página inicial e reinicia a fila; estas verificações gerais não provam o acesso ao módulo. A configuração de exemplo aponta para `/etc/investmoz-deploy/schema-contract.json` e `/etc/investmoz-deploy/delivery.json`. Os ficheiros instalados podem divergir do repositório e devem ser lidos e comparados. Não se deve remover o gate nem converter o deploy em migração global automática.
**Delta exacto de schema.** Uma única migration: `database/migrations/2026_10_22_230000_create_portal_tenders.php`. Não altera tabelas existentes, extensões, utilizadores, papéis, grants, documentos ou dados territoriais; não contém seeds nem publicação de conteúdo.
| Tabela nova | Colunas | Chaves e relações efectivamente declaradas |
|---|---|---|
| `portal_tenders` | `id` UUID; `draft` JSONB obrigatório; `version` integer com default 1; `published_version_id` UUID anulável; `created_by` bigint obrigatório; `created_at`, `updated_at` timestamptz anuláveis | PK `id`; FK `created_by → users.id`, `RESTRICT` na eliminação |
| `portal_tender_versions` | `id` UUID; `tender_id` UUID; `version` integer; `act` varchar(24); `payload` JSONB; `reason` text; `actor_id` bigint; `published_at` timestamptz anulável; `created_at` timestamptz obrigatório com default actual | PK `id`; UNIQUE (`tender_id`,`version`); FK `tender_id → portal_tenders.id` e `actor_id → users.id`, ambas `RESTRICT` na eliminação |
| `portal_tender_watches` | `tender_id` UUID; `user_id` bigint; `seen_version_id` UUID anulável; `created_at`, `updated_at` timestamptz anuláveis | PK (`tender_id`,`user_id`); FK para `portal_tenders.id` e `users.id`, ambas `RESTRICT` na eliminação |
`published_version_id` e `seen_version_id` não têm FK nesta candidata; a associação de publicação é verificada pela aplicação, incluindo pertença da versão ao concurso. O histórico é imutável pelas operações da aplicação; esta migration não acrescenta trigger de imutabilidade. Índices novos: os das três PK e do UNIQUE indicado. O executor Laravel regista também a migration na tabela `migrations` existente. Confirmar tipos e restrições no catálogo PostgreSQL após instalação, não apenas a existência de uma tabela.
Dependências já existentes: PostgreSQL/JSONB, `users`, entidades `organizations`, `opportunities`, `projects`, `sectors`, `geo_locations`, biblioteca `portal_resources`/`documents` e relações documentais, `audit_events`, autenticação/verificação/MFA e autorização documental/editorial conforme a conta. A FK de instalação depende directamente de `users`; as restantes tabelas são dependências de execução do cadastro, prévia, consulta ou publicação. Manter os originais no armazenamento persistente privado e o scanner/fila da biblioteca operacionais; a publicação de anexos exige o scan e os direitos já aprovados. Sem nova dependência Composer/NPM ou serviço externo. `composer.lock` e `package-lock.json` não mudam. As provas locais de `ProcurementTest` usam a base QA nominal e ClamAV real; não adicionar esse teste ao CI remoto sem adaptar a sua guarda de ambiente e disponibilidade do scanner. O pipeline conserva a sua suite isolada existente; não se repete a bateria QA aceite.
**Sequência proposta, com pontos de paragem verificáveis:**
1. **Revisão e identificação.** Rever `1bcf34c0` e este plano. Confirmar SHA integral da fonte, revisão/release actualmente servida, configuração real do pipeline, contrato actual, ligação à base e estado da migration. Conferir as contas reais já existentes contra `DocumentManagementAccess`/`Procurement::access`: gestão completa ou designação editorial existente, email verificado e requisitos MFA aplicáveis; auditor e conta simples sem escrita. Não inferir acesso por nome do papel nem criar designações. Se uma conta ainda não existe, registar só essa pendência e continuar com as já autorizadas.
2. **Coordenar a janela antes do merge.** Pausar o observador `teste-investidor-deploy.path` e impedir novo processamento durante a operação; aguardar o serviço em curso e coordenar com `delivery.lock`. Preservar os pedidos em fila e reconciliar qualquer integração concorrente antes de retomar. Não parar à força um build nem manipular a release servida. Confirmar `PROCUREMENT_ENABLED=false` na configuração efectiva; não colocar esta variável no ficheiro de flags CI, cujo validador só aceita prefixos `PORTAL_`/`INVESTMOZ_`.
3. **Backup recuperável.** Guardar dump consistente da base de destino, cópia/snapshot do armazenamento persistente privado e público e manifesto de hashes, revisão/release anterior, contrato, configuração e recibos. Configuração e segredos ficam em local privado. Ensaiar o restauro desse backup numa instância isolada, sem substituir a base activa nem publicar ficheiros; conferir integridade, tabelas e amostra de binários. A prova de restauro antiga não substitui o backup desta janela. Registar hora/identidade da base e preservar os recibos.
4. **Instalar schema sem activar.** Preparar a fonte exacta revista fora da árvore servida, sem fixtures QA, com dependências do lock e ligação autenticada ao destino conferida. Pela sessão técnica autenticada e com os privilégios de instalação existentes, executar **apenas** `php artisan migrate --path=database/migrations/2026_10_22_230000_create_portal_tenders.php --force` nessa fonte/configuração. Não executar `migrate` global, `migrate:fresh`, seeders ou scripts de demonstração. Se a migration já estiver aplicada, conferir o schema e não duplicar a instalação; tabelas parciais ou divergentes exigem reconciliação concreta, não DROP/recriação. Confirmar registo da migration e as três tabelas vazias, incluindo versões e acompanhamento. A release antiga continua compatível com estas tabelas adicionais.
5. **Actualizar o contrato de migrations.** Guardar o contrato anterior e acrescentar exclusivamente o caminho/hash normalizado desta migration, após a instalação conferida. Comparar o contrato completo com a fonte revista: qualquer outra diferença deve ser explicada antes de prosseguir. Não regenerar/aprovar cegamente um contrato para o HEAD mais recente. Este passo é uma operação técnica explícita e não corre automaticamente por Git.
6. **Integrar e transportar código, ainda desligado.** Integrar pelo processo Git após a revisão, identificar o SHA resultante em `main` e retomar o pipeline/pedidos preservados. Conferir que o contrato corresponde exactamente à fonte resultante; só então o build pode passar. Usar o build isolado e a promoção atómica existentes, sem editar ficheiros da release servida. Confirmar recibo, `/build/revision.json`, página inicial, biblioteca e gestão única. O módulo permanece desligado e as tabelas vazias; a existência do código não constitui activação.
7. **Activar separadamente.** Com código/schema compatíveis conferidos, definir `PROCUREMENT_ENABLED=true` na configuração persistente do destino por acção técnica autenticada. Actualizar a cache efectiva de configuração e recarregar processos persistentes conforme a configuração instalada. O pipeline versionado não faz `config:cache` para a configuração real: não presumir que editar `.env` basta quando houver cache. Se se gerarem caches dentro de uma release sujeita a `tree_hashes`, reconciliar os hashes pelo mecanismo de entrega antes de novo deploy/rollback, sem mascarar outras diferenças. Não executar testes com `RefreshDatabase` no destino.
8. **Conferir só o delta no destino.** Aplicar a lista abaixo, registar revisão, URLs, resultados e actor autenticado sem credenciais. Entregar o link único `https://teste-investidor.tempo.co.mz/gestao-conteudos` e o guia desta secção com os links QA substituídos pelos correspondentes do destino. Não declarar disponibilização enquanto esta passagem não estiver concluída.
**Verificação no destino sem publicar anúncios reais como efeito da instalação:**
- Sem sessão: `/concursos` responde 200, incluindo catálogo vazio quando não houver anúncios; navegação pública mostra a secção; não surge autenticação HTTP adicional. UUID de rascunho/inexistente não permite obter ficha ou relatório públicos; anexos privados continuam inacessíveis.
- Com conta real já autorizada: gestão única mostra **Novo concurso/anúncio** e **Gerir concursos e anúncios**; abrir formulário, guardar um rascunho de verificação claramente identificado, recuperar e pré-visualizar, sem publicar. Registar ID e preservar o rascunho/histórico; não o converter num anúncio real. Conferir scanner/biblioteca existentes por diagnóstico e estado, reutilizando as provas de upload aceites.
- Conta simples: pode abrir a consulta e área de acompanhamento, mas recebe recusa na escrita/gestão; auditor não recebe escrita. Utilizador sem sessão é encaminhado para autenticação nas rotas de gestão. Não alargar grants para passar a prova.
- Se já houver anúncio autorizado no destino, consultar sem sessão ficha, relatório, anexo autorizado e filtros/retorno. Se não houver, a consulta positiva de conteúdo publicado mantém a prova QA aceite e o catálogo vazio é o resultado esperado no destino; não importar exemplos nem publicar um anúncio só para preencher o catálogo. Prorrogação/publicação/resultado já estão cobertos em QA e não são repetidos sobre anúncios reais durante a instalação.
- Conferir que instalação/activação não criou qualquer versão com `published_at` preenchido, não mudou `published_version_id` de anúncios existentes e não alterou documentos/grants. Anúncios que a equipa publicar posteriormente são acções distintas, identificadas pela auditoria.
**Reversão com preservação.** Em falha de instalação antes do merge, manter flag desligada e release antiga; conservar tabelas eventualmente criadas até diagnosticar a falha e repor o contrato anterior se necessário. Em falha após disponibilização, desligar primeiro a flag na configuração efectiva e preservar um novo snapshot/dump dos dados e ficheiros actuais. Preferir correcção progressiva. Se for preciso reverter código, usar o rollback atómico existente do pipeline para a release anterior compatível, com recibo e configuração desligada; conservar as três tabelas, `migrations`, `audit_events`, subscrições e armazenamento partilhado. Esse rollback de código não restaura a base. Conservar o contrato compatível com a linha de código que voltará a ser entregue e documentar qualquer reposição do ficheiro de contrato; a verificação por hashes continua obrigatória.
Não executar `down`, `migrate:rollback` global nem restaurar por cima da base activa o backup anterior à instalação: isso eliminaria anúncios, actos, acompanhamentos, auditoria e ficheiros criados depois. O `down()` da candidata recusa a remoção quando há concursos, mas essa guarda não substitui o plano de preservação. Um restauro extraordinário exige primeiro recuperar numa instância separada e reconciliar os dados posteriores, conservando IDs, versões, relações, hashes e originais; só trocar o destino com um plano próprio revisto. Desligar o módulo torna-o temporariamente indisponível, não elimina nem retira selectivamente anúncios. Ao reactivar a release corrigida, recuperar e conferir os dados existentes.
Origem dos limites: QA/revisão antes de integração e ausência de publicação real na instalação são decisões expressas da Tempo; backup/restauro e autenticação de alterações no destino são requisitos permanentes existentes; pausa coordenada, schema antes de código, flag e contrato são escolhas técnicas impostas pela compatibilidade observada do pipeline. Não constituem nova cadeia organizacional de aprovação. Depois da instalação, as operações correntes de cadastro, prévia, publicação, prorrogação e actualização permanecem na gestão única, sem comandos ou Codex por concurso. As únicas intervenções técnicas deste plano são a instalação/activação e eventual recuperação da plataforma.
### Instalação concluída na homologação — 22/10/2026
Autorização expressa da Tempo nesta conversa: **«pode instalar»**, abrangendo a candidata `1bcf34c0` e o procedimento `e46f9731`. A instalação foi executada por SSH autenticado já disponível; a verificação da interface foi feita pela conta real Osmane Nala, ID 15, após renovação de login/MFA pelo utilizador. Não houve criação de contas, papéis ou aprovadores. A ausência de outras contas não bloqueou esta autorização existente. O guia continua sem declaração de conferência/aceitação pela Tempo.
Sequência efectivamente concluída: pausa coordenada do observador de deploy → backup → restauro isolado conferido → migration selectiva com flag desligada → conferência do schema e contrato → integração/push Git → build/promoção automática → activação separada. Integração funcional `f4d0970917e7e8d141f74c136b66138b929141fc`, confirmada em `/build/revision.json`. Activação às **22h51 de 22/10/2026 (África/Johannesburgo; 20h51 UTC)**. Os commits documentais posteriores conservam o mesmo código funcional. O pipeline está novamente activo e conserva o gate do contrato de migrations; não foi alterado para executar SQL de produção.
Backup privado no servidor: `/var/backups/investmoz-procurement-20261022/backup`. Dump SHA-256 `69de817b288ecb25db4b341375475f2408a47642a8ba69d338b217dcc982854f`; arquivo de ficheiros/configuração `7b75c22e730b5600d6e97e1c6dbb4cd93982bdd47e9dd2a63de2359974132674`. Restauro realizado num PostgreSQL isolado em container sem rede, porta interna 55441: **171 tabelas**, contagens e checksums iguais ao snapshot exportado. Arquivo conferido: **341 ficheiros**, incluindo configuração privada; preservados os **340 ficheiros de conteúdo** no destino. Não foi copiada uma base QA para a homologação.
Schema instalado: **três tabelas, 21 colunas, três PK, cinco FK e um UNIQUE**, correspondentes à migration revista; registo em `migrations` conferido. A conferência inicial do executor tinha uma contagem de colunas incorrecta (22); foi corrigida para as 21 efectivamente declaradas e conferidas antes de actualizar o contrato, sem repetir ou desfazer a migration. Não se alteraram grants. Comparação dos dados preexistentes antes da prova de rascunho: apenas `migrations`, `cache` e `sessions` diferiam do backup; utilizadores, atribuições, biblioteca, oportunidades, cadastro territorial e geometrias permaneciam iguais por checksum. Scanner ClamAV real disponível, assinaturas de 22/10/2026.
Conferência do delta no destino:
- `/concursos` responde 200 sem sessão ou autenticação HTTP adicional; catálogo vazio é esperado, porque não foram publicados anúncios. Gestão sem sessão redirecciona para login. Conta simples existente não tem autorização de escrita; recusas por perfil e operações editoriais conservam as provas QA aceites, sem criar uma conta de teste no destino.
- Gestão única da conta real apresenta **Novo concurso/anúncio**. Rascunho `b970f0f0-9257-42e1-8365-3358237b55db`, referência `INSTALL-CHECK-20261022`, claramente identificado como **VERIFICAÇÃO DE INSTALAÇÃO — NÃO PUBLICAR**: guardado, recuperado pela lista e pré-visualizado na interface. Um acto de preparação auditado (`tender_draft_saved`, actor 15). Preservado como rascunho privado; não é um concurso real.
- Ficha e impressão desse rascunho devolvem 404 sem sessão; download de documento privado devolve 404. Não se alterou a disponibilidade dos documentos existentes. **Uma preparação, uma versão de rascunho, zero versões publicadas e zero acompanhamentos.** Nenhum anúncio real foi publicado pela instalação ou como prova. Não foram transportados exemplos QA.
- Publicação/prorrogação/resultados/cancelamento positivos conservam os 8 testes/285 asserções e a passagem visual QA aceites; não foram repetidos sobre anúncios reais na homologação. O build de entrega executou as suas verificações isoladas normais.
Recibo resumido versionado: [procurement-installation-receipt-20261022.json](procurement-installation-receipt-20261022.json). Recibos técnicos completos, logs e backups permanecem privados; capturas e recibo local em `outputs/procurement-install-20261022/`. A reversão continua a ser desligar a flag e, se necessário, reverter código compatível mantendo tabelas, versões, auditoria e armazenamento; não substituir dados novos pelo backup anterior.
**Links finais e guia de operação:** entrar na [gestão única](https://teste-investidor.tempo.co.mz/gestao-conteudos) → **Novo concurso/anúncio** → preencher regime/edição, entidade, objecto, calendário/fuso e canal → **Guardar rascunho**. Recuperar em **Gerir concursos e anúncios → Recuperar e editar**. Carregar/editar originais pela biblioteca ligada no formulário, acompanhar o scan e autorizar a divulgação dos anexos; voltar ao editor e actualizar as opções. **Preparar prévia pública** → rever conteúdo e motivo público → **Publicar este acto**, quando a equipa pretender publicar um anúncio autorizado. Para actualizar ou prorrogar, guardar a revisão e escolher o acto correspondente; a prorrogação mostra prazo anterior e novo. Resultado ou cancelamento seguem o mesmo percurso. O rascunho de instalação não se destina a publicação.
Consulta: [Concursos e anúncios](https://teste-investidor.tempo.co.mz/concursos). Abrir ficha para documentos e relatório imprimível; uma conta simples pode acompanhar, sem apresentar proposta. Documentos, oportunidades, divulgação promocional e concursos mantêm as distinções do guia anterior. **Não restam comandos ou intervenção do Codex para cada concurso** neste âmbito; a instalação e activação técnica estão concluídas. Geometrias e permissões foram preservadas.
### Legado — correspondência integral e avanço privado QA, 22/10/2026
Fonte do saldo: conferência documental fornecida pela Tempo e [InvesteMoz — entregas, Fintech e classificação](https://chatgpt.com/space/page_d6f090dba9048191a92c9175b3605dee), consultada nesta passagem; originais e reconciliação de 15/10 preservados. O CSV antigo ainda tinha 17 publicados/31 por integrar. Reconciliados os três artigos posteriores, sem os duplicar: **20 publicados, 28 por integrar na homologação, um vazio e um sem título = 50 identidades**. Os 29 recursos da biblioteca e as onze variantes de URL não são contagens de artigos distintos.
Lista única para a Tempo: [título original → endereço original → endereço no novo portal → estado → pendência](../v2/auditoria-portal/legado-correspondencia-integral.md). Actualizado também o [CSV existente dos 50 artigos](../v2/auditoria-portal/legado-conteudos-reconciliados.csv); mantém IDs, títulos, origens, hashes e referências anteriores, com campos explícitos para o estado consolidado e data da conferência. Não se criou nova matriz/backlog. Endereços novos ausentes ficam em branco; não foram fabricadas páginas para os conteúdos ainda não disponibilizados.
**Nova conferência pública, não nova aceitação funcional:** 20/20 páginas na homologação responderam sem login, com componente `portal/legacy`, ID legado, origem e corpo não vazio correspondentes. A identidade não foi deduzida apenas de HTTP 200 nem do título. IDs da biblioteca foram obtidos da resposta real do destino, sem transportar IDs QA. Consulta HTTP em 22/10/2026 às 23h05 (21h05 UTC). Não é revisão visual integral de cada artigo nem confirmação de actualidade de todas as afirmações. Imagens anteriores não foram reimportadas ou reanalisadas; as referências públicas das fichas foram registadas, sem repetir as provas aceites dos binários.
**Cartões educativos:** a branch `codex/educational-content-cards` já é ancestral de `main`, e o componente com imagem maior/resumo/«Ler o guia» está servido. Conferência visual no destino [Explorar Turismo](https://teste-investidor.tempo.co.mz/explorar/turismo): 12 cartões/links «Ler o guia». Corrige a indicação histórica de revisão ainda pendente, sem atribuir à Tempo uma conferência que não fez. Captura `outputs/legacy-correspondence-20261022/guides-homologation.png`.
**28 conteúdos avançados em QA, sem publicar:** dois sectoriais, oito de Trabalho Doméstico e 18 históricos/noticiosos. Preservado o corpo HTML de cada captura, hash do corpo, ID, autor, categoria, datas e origem. Derivadas JSON identificadas pelo formato existente, armazenadas privadamente; imagens não resolvidas não foram substituídas por um fundo genérico nem marcadas como disponíveis. O mecanismo existente `documents:import-batch` foi reutilizado exclusivamente em `simulator_fiscal_delta_20261021_test`, porta 55441, com guarda da identidade real. Não foi alargado o importador activo ou o seu âmbito nominal. Conta sintética QA já existente 239; sem novo grant persistente. **28 rascunhos internos, 28 scans reais limpos e 28 reutilizações por SHA-256 na repetição**, sem duplicações; publicações e atribuições preservadas.
A primeira passagem importou dez e detectou colisões de identidade nos restantes: o manifesto usava emissor/data/versão iguais para artigos distintos. Corrigida a versão documental dos itens restantes para incluir o ID original do artigo; os dez já criados foram reutilizados por bytes. Sem apagar/recriar originais, simular scan ou classificar artigos como diplomas oficiais. Esta era uma escolha de metadados da preparação, não uma decisão organizacional da Tempo nem uma razão para impedir o cadastro. O importador não foi alterado e os títulos originais foram preservados. Preparações, manifesto, logs e recibo completos mantidos em caminhos privados QA; o recibo versionado contém somente metadados de rastreabilidade.
**Lacunas originais conferidas na rede:** [Mercado Financeiro, ID 171994](https://investemoz.tempo.co.mz/mercado-financeiro), HTTP 200, corpo vazio, hash igual à captura. [Post 74364](https://investemoz.tempo.co.mz/74364-2), HTTP 200, título vazio, mesmo corpo capturado (274 caracteres HTML, 21 úteis no inventário). Não se encontrou conteúdo/título novo a recuperar. A resolução editorial destes dois exemplares depende do texto/título da Tempo; não se inventou um título nem se usou Fintech/imagem para preencher o artigo vazio. Não bloqueiam os outros 48.
**Saldo de integração conservado:** os 28 rascunhos QA ainda exigem fechar edição/leitor e entrega/publicação por lotes na homologação. Trabalho Doméstico e notícias mantêm âmbito/data históricos; não foram convertidos em orientação jurídica actual, regras executáveis ou oportunidades vigentes. As dependências de 15/10 estão identificadas como históricas, não como novos requisitos de validação. Nenhum conteúdo novo foi publicado no destino, não houve alteração de schema, mapas, permissões ou integração em `main` nesta passagem. A integração integral continua aberta; não confundir preparação privada QA com publicação dos 28.
Evidência consolidada: [recibo de correspondência, consulta e importação QA](../v2/auditoria-portal/legado-correspondencia-recibo-20261022.json). Ferramenta `tools/legacy-correspondence-audit.py`; `--reuse-http` regenera a lista sem repetir as consultas públicas. Os hashes das capturas/respostas são distintos dos hashes dos corpos/originais; não foram usados para alegar uma nova edição editorial.
### Legado — edição e leitura dos 28 rascunhos em QA, 22/10/2026
Actualiza o saldo anterior: **20 públicos já conferidos, 28 rascunhos privados com editor/leitor disponíveis em QA e dois originais incompletos**. Não houve nova importação, repetição da conferência dos 20, integração em `main`, transporte ou publicação na homologação nesta passagem. A Tempo não declarou ter conferido o guia local ou esta candidata.
**Guia curto da equipa.** Em [gestão única QA](http://127.0.0.1:18580/gestao-conteudos), abrir o acervo e localizar o título. **Editar artigo** recupera o corpo do original, a origem e as datas; corrigir o texto e escolher conservar a imagem de origem, seleccionar uma imagem pública verificada da biblioteca ou não mostrar imagem. **Pré-visualizar** mostra as alterações ainda sem guardar e não publica. **Guardar edição** cria uma revisão, preserva o original e retira eventual publicação até nova decisão expressa. **Ler revisão guardada** recupera a edição persistida. Metadados, classificação/direitos, histórico e publicação continuam no acervo ligado pelo editor; **Publicar** é uma acção distinta, sujeita às permissões e campos já existentes. Para actualizar um artigo publicado, guardar a revisão, rever a prévia e publicar novamente. A consulta pública usa `/artigos/{id-do-destino}` e o acesso pela biblioteca; rascunhos não ficam acessíveis por esse endereço.
O leitor mantém a estrutura HTML sanitizada do original enquanto o corpo não for alterado; uma correcção textual fica como edição separada, sem reescrever o ficheiro capturado. Datas e autor de origem continuam identificados. Conteúdos históricos não se tornam oportunidades actuais, diplomas oficiais ou regras de cálculo. A revisão QA do artigo 62413 contém uma nota de demonstração explicitamente marcada **QA**, guardada pela interface: deve ser revista/corrigida antes de publicação real; não foi removida silenciosamente do pacote.
**Imagens e lacunas.** Preservadas as 16 referências a imagens no corpo e a referência de capa dos 28 originais. Não se descarregam automaticamente imagens externas. Nesta QA não há exemplar público verificado da capa comum: a prévia informa a ausência e conserva a referência. A capa já aceite no destino deve ser reutilizada pelo SHA-256 `28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa`, sem confundir igualdade de URL com igualdade de bytes. Pode seleccionar-se outra imagem autorizada pela interface; nenhuma imagem foi marcada como disponível sem binário verificado. Os dois casos incompletos na origem ficam fora deste lote.
**Permissões e origem das restrições.** Reutilizados os gestores e as designações editoriais existentes, email verificado, MFA aplicável e exclusão de escrita para auditor/conta simples. Nenhum papel ou aprovador novo. A inclusão destes 28 originais no âmbito editorial decorre do pedido expresso da Tempo. A correspondência por hash exacto dos originais é uma escolha técnica: não depende de IDs ou etiquetas de importação QA que mudem no destino. Validação dos bytes, scan e classificação/direitos seguem requisitos anteriores; controlo de versão e prévia privada são escolhas técnicas para evitar sobreposição de edições e publicação acidental.
**Evidência delimitada.** Seis testes específicos, **1.000 asserções**, passaram na base isolada `simulator_fiscal_delta_20261021_test:55441`, com transacções revertidas. Percorrem os 28 editores/prévias, edição/recuperação/histórico, conflito de versão, publicação explícita e retirada, classificação, imagem verificada/retirada, sanitização e permissões nominais/MFA. A prova de publicação dos testes foi revertida; os 28 continuam privados. TypeScript e build QA passaram. Na interface, conta sintética QA existente guardou/recuperou a revisão 2 do artigo 62413; leituras de Alumínio “Verde” e do guia de Trabalho Doméstico demonstradas. Não houve alteração de contas reais. Capturas e logs em `outputs/legacy-editorial-20261022/`; [recibo resumido](../v2/auditoria-portal/legado-editorial-qa-20261022.json).
**Preparação concreta da entrega privada.** `outputs/legacy-editorial-20261022/legacy-editorial-private-v2.zip`, 85 614 bytes, **57 entradas**: 28 originais, 28 snapshots/históricos de proveniência e manifesto. SHA-256 `177c50d1cc3b1610e62123a2f8b532711a15f875aac2ee0e372636c2f747e8a2`. Conferidos os 28 hashes e a integridade do ZIP; zero imagens binárias incluídas e zero escritas de base pelo exportador. Manifesto com `publish=false`, edições actuais e vínculos de imagem por hash; sem credenciais ou cópia da base. `tools/prepare-legacy-editorial-delivery.php` só exporta a QA nominal, não instala/importa no destino. Originais e histórico completo ficam privados; IDs/actores QA são proveniência, não identidades ou autores de auditoria no destino.
**Sequência para homologação, ainda não executada:**
1. Rever e integrar a candidata pelo processo Git existente. Não há migration, alteração de schema ou nova dependência de pacote; o deploy de código não transporta estes conteúdos nem publica artigos.
2. Antes de escrever no destino, conferir revisão servida, identidade da base e backup consistente de base/armazenamento com restauro isolado demonstrado para a janela. Transportar o ZIP para armazenamento privado, fora da árvore pública, e conferir o hash. Preservar o recibo e o manifesto.
3. Com autenticação e permissões existentes, usar o cadastro/upload da biblioteca para cada original ainda ausente, com scan real e classificação privada. Resolver por hash e identidade/versão: reutilizar originais iguais, registar IDs reais e parar apenas o item com conflito, sem sobrescrever. Não repetir a importação QA nem copiar a sua base. Não usar os importadores nominais de Agronegócio/lotes antigos ou um `--actor` para contornar os limites activos; o exportador não alarga esses executores.
4. Recuperar os recursos pelo portal; aplicar metadados/edição actual pelos formulários autenticados, usando a versão do destino e criando histórico atribuído ao actor real. Conservar os snapshots QA privadamente como proveniência, sem inserir actores QA na auditoria activa. Reutilizar a capa aceite por hash; referências sem binário mantêm aviso. Conferir **28 privados, zero publicações**, correspondência original → recurso real e hash, revisão guardada e acessos previstos. Actualizar a lista única com os endereços efectivamente demonstrados.
5. A equipa decide e executa a publicação individual no portal, após rever/corrigir/pré-visualizar. Transporte privado, autorização dos metadados e publicação são etapas distintas. Não publicar artigos como efeito da instalação ou da conferência do transporte. Verificar só o delta do destino, sem repetir os 20 aceites ou as provas QA.
**Reversão e dependências remanescentes.** Para retirar conteúdo, usar a retirada pela interface e preservar originais, revisões e auditoria. Se for necessário reverter código, manter dados e ficheiros; não restaurar um backup antigo por cima de edições/publicações posteriores. Preservar novo snapshot e reconciliar o delta antes de qualquer restauro. A revisão/integração Git, transporte privado, backup/restauro e upload/scan autenticado iniciais ainda são trabalho de entrega; não estão declarados concluídos nem constituem tarefas recorrentes de cada edição. Depois de disponibilizado este delta, rever, corrigir, pré-visualizar, guardar, recuperar, publicar e retirar estes conteúdos não exigem comandos ou Codex. A revisão editorial efectiva dos textos pela equipa continua distinta da demonstração do percurso. Geometrias não foram alteradas.
### Legado — cadastro privado concluído na homologação, 22/10/2026
**Actualização da entrega anterior:** a candidata `fb4e7d89` foi revista tecnicamente e integrada pelo Git em `65eddf6bc7025c81cbd7b0c5918b6ae8bdb1c1f6`, revisão funcional servida durante esta conferência. Sem migration ou novas dependências. Backup anterior às escritas: dump SHA-256 `8658b5fa0d1ef16c9fc9be90969badde6007443aa474fe4a9377f16d80fe6e92`, arquivo de ficheiros/configuração `1d98b6200ffae11bb07dc600daa6416b3b1b90735de42791858a07a0caa63552`; restauro isolado com 174 tabelas e 341 ficheiros conferidos. Backup e pacote continuam privados em `/var/backups/investmoz-legacy-editorial-20261022/`. O transporte já concluído não foi repetido nesta retoma.
**28 originais cadastrados, 28 recursos privados, zero publicações.** Reutilizado no servidor o pacote v2 com SHA-256 `177c50d1cc3b1610e62123a2f8b532711a15f875aac2ee0e372636c2f747e8a2`. A ingestão usou os endpoints HTTPS existentes de upload, cadastro e edição, com referência à sessão real recente de Osmane Nala (actor 15), obtida após login e MFA normais; sem criar sessão/MFA, atribuir papéis ou escrever directamente nas tabelas de conteúdo. O operador autenticado executou esta passagem inicial a partir dos ficheiros já transportados; a equipa não precisa deste executor para rever ou publicar os recursos agora disponíveis. O limite de uploads foi respeitado. Uma resposta intermédia não pôde ser descodificada pelo invólucro técnico; a retoma encontrou os originais por hash e continuou sem nova cópia dos já cadastrados. Não foi atribuída uma causa não demonstrada a essa interrupção.
Cada original foi resolvido por hash, com conferência de unicidade antes do cadastro; a classificação permaneceu `internal`, os direitos/estado documental anteriores não foram convertidos em autorização de publicação e os scans reais ficaram limpos, com hash analisado igual aos bytes actuais. IDs do destino 32–59, distintos dos IDs QA. A identidade de versão inclui o identificador do original, evitando colisões entre artigos da mesma data. Os snapshots QA permanecem como proveniência no pacote privado; o histórico activo foi criado pelos endpoints com o actor real, sem importar identidades QA como autores da auditoria. A edição QA do artigo 62413, incluindo a nota explicitamente marcada QA, foi conservada para revisão da equipa.
**Conferência operacional do delta, concluída às 23h54 de 22/10/2026 (África/Johannesburgo; 21h54 UTC):** 28/28 encontrados pela pesquisa de rascunhos; 28/28 editores e leituras privadas responderam 200, com identidade, fonte e corpo recuperado correspondentes; 28/28 endereços públicos `/artigos/{id}` devolveram 404 sem sessão. Na interface, a conta real pesquisou «15/30», abriu o artigo 62413, preparou a prévia, guardou o mesmo corpo como revisão 3 e recuperou essa revisão, sempre privada. A gravação de conferência não acrescentou texto de demonstração ao corpo. Não se repetiram as provas QA ou as consultas HTTP dos 20 artigos públicos.
Preservação por comparação dos registos preexistentes: **32 documentos, 31 recursos, 63 revisões e 19 associações**, incluindo os 20 artigos públicos, permaneceram iguais. Também preservados 17 utilizadores, atribuições de papéis, par de superutilizadores, aprovações de segurança, 1.921 entradas territoriais e 408 geometrias. Nenhuma publicação foi feita por esta entrega. As 16 referências de imagens no corpo permanecem discriminadas; a capa comum é o exemplar anterior, explicitamente referenciado nos originais e reutilizado pelo hash exacto `28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa`, não uma imagem presumida. Referências sem exemplar verificado não foram preenchidas com substitutos.
**Guia da equipa no destino:** abrir a [gestão única](https://teste-investidor.tempo.co.mz/gestao-conteudos) → **Gerir conteúdos, versões e histórico** → escolher **Rascunhos**, pesquisar o título e abrir **Editar artigo** ou **Ler / pré-visualizar artigo**. Corrigir o texto e o tratamento de imagem → **Pré-visualizar alterações** → **Guardar artigo como rascunho** → **Ler revisão guardada**. Usar **Voltar ao acervo para metadados e publicação** para corrigir título, descrição, origem, enquadramento, acesso e direitos. **Publicar** continua separado, conforme a autorização e os campos existentes, quando a equipa decidir publicar. Guardar uma edição de um artigo publicado retira-o até nova publicação expressa. Não há comandos ou intervenção do Codex neste percurso corrente.
Recibo: [cadastro, acessos e preservação no destino](legacy-editorial-installation-receipt-20261022.json). A [lista única dos 50](../v2/auditoria-portal/legado-correspondencia-integral.md) e o CSV existente contêm os endereços **privados** efectivos dos 28; não são links de artigos já publicados. Capturas locais em `outputs/legacy-editorial-install-20261022/`, incluindo `editor-recovered-homologation.png`. Mantêm-se dois originais incompletos e a revisão editorial dos textos pela equipa. A Tempo não declarou ter conferido commit, pacote ou guia. **A carteira vazia e os projectos âncora continuam como falha separada, sem causa confirmada; esta entrega não a resolve.** (Saldo histórico desta entrega de 22/10; superado pela reposição auditada e reconciliação de 23/10 descritas acima. Não representa o estado actual.)
@@ -0,0 +1,174 @@
{
"status": "installed_in_homologation_delta_verified",
"date": "2026-10-23",
"target": "https://teste-investidor.tempo.co.mz",
"served_commit": "8036b9b071229dbf7e85ea748db70abcbeefc379",
"functional_commits": [
"1afdfee8",
"2856240c"
],
"previous_main": "55136b446deb710adf58ce2460e571bb19e94375",
"public_revision": {
"commit": "8036b9b071229dbf7e85ea748db70abcbeefc379",
"branch": "main",
"built_at": "2026-10-23T16:46:11.203132+00:00",
"manifest_sha256": "6021421c4ab12146c5fae9c8966e3dae5c8bd1361132e12184e65e00b7e8951e"
},
"installer": {
"status": "installed",
"commit": "8036b9b071229dbf7e85ea748db70abcbeefc379",
"release": "/var/www/investmoz-homologacao/releases/8036b9b071229dbf7e85ea748db70abcbeefc379-5e851af2",
"previous_release": "/var/www/investmoz-homologacao/releases/55136b446deb710adf58ce2460e571bb19e94375-cad6589e",
"database_migrations": 0,
"content_imports": 0,
"requested_commits": [
"8036b9b071229dbf7e85ea748db70abcbeefc379"
],
"finished_at": "2026-10-23T16:46:13.798467+00:00"
},
"pipeline_summaries": [
"OK (1 test, 3106 assertions)",
"built in 24.30s",
"OK (57 tests, 309 assertions)",
"# tests 214",
"# pass 214",
"# fail 0"
],
"preservation": {
"fast_forward": true,
"force": false,
"protected_files": {
"app/Services/InvestmentComplements.php": true,
"resources/js/components/investment-complements.tsx": true,
"docs/investmoz/unification/continuity-corrections-installation-receipt-20261023.json": true
},
"matrix_changed_rows": [
"AUD-REL-02",
"AUD-LEG-06",
"AUD-SIM-INT03",
"AUD-SIM-INT06"
],
"unrelated_matrix_records_preserved": 3419,
"schema_config_fixtures_shipped": false
},
"native_saved_pdf": {
"actual_file": "C:\\laragon\\www\\portal-investidor\\outputs\\multi-opportunity-fix-20261023\\Relatório — SYNTHETIC QA - escolha comum aos tres consumidores.pdf",
"sha256": "010eb4ea12015563c1b2f8bc255b1078c276f824c50690732541d5bffe34b8a6",
"pages": 10,
"revision": 13,
"checks": {
"second_opportunity": true,
"manica": true,
"land_requirement": true,
"sector_requirement": true,
"human_state": true,
"technical_state_absent": true,
"first_location_absent": true,
"total": true,
"secured": true,
"gap": true
},
"visual_review": {
"status": "passed",
"pages_inspected": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
],
"pagination": "1/10 through 10/10; A4; repeated table headers; no clipped rows or visible overlaps",
"last_page": "sparse final page contains final source rows and annex statement; not blank"
},
"discovery": {
"machine": "ONALA",
"user": "macbook",
"session": "Console",
"extension": ".pdf",
"size_bytes": 191637,
"requested_basename_not_found": "candidata-1afdfee8.pdf",
"actual_basename": "Relatório — SYNTHETIC QA - escolha comum aos tres consumidores.pdf",
"resolution": "existing file discovered by listing the exact requested directory; no rename, copy or PDF regeneration"
},
"regenerated": false
},
"installed_delta": {
"real_opportunity": 53,
"scope": {
"case_id": "958df138-7980-4685-8389-fa90be02ed6b",
"case_version": 5,
"context_revision": 1,
"simulator_opportunity": "opportunity:53",
"snapshot_choices": [
"opportunity:53"
],
"snapshot_territory": "Sofala",
"land_need": "no",
"land_step": {
"code": "S10",
"title": "Regularização da terra",
"state": "dispensed",
"applicability": "not_needed_for_preparation",
"reason": "",
"context_review_required": true
},
"requirements_scopes": [
"opportunity:53"
]
},
"checks": [
"new fiche links preserve opportunity53",
"dossier explicitly selects same opportunity and keeps tool query scope",
"authenticated context GET returns canonical simulator opportunity53 and its own requirements",
"existing no-land decision hides land action in the inspected dossier",
"15 changed functional file hashes equal Git release sources"
],
"case_version_unchanged_from_prior_receipt": true,
"requests_created_updated_submitted": 0,
"accepted_business_tests_repeated": false,
"qa_second_opportunity_cases_reused": true
},
"remaining_scope": {
"anchors": "analysis6985f82e: two implementation gaps, territorial reconciliation and directed interest/authentication proof; not implemented by this delivery",
"statistics_and_AI": "deferred"
},
"evidence": [
{
"path": "outputs/treatment-install-20261023/integration-preservation.json",
"sha256": "c6fcae5f64986d13216948589a35d1fa2d9f62229f4bd7a8a8c9f559f463fd6b"
},
{
"path": "outputs/treatment-install-20261023/installer-status.json",
"sha256": "f7875ffe9fa47092e5f0288ecbd97f4f016413628b2faed6815977287c26461e"
},
{
"path": "outputs/treatment-install-20261023/public-revision.json",
"sha256": "23325ab4cce9fced625b237c9793e3af309d923f12abbd047de96670611c8cac"
},
{
"path": "outputs/treatment-install-20261023/installed-hashes.json",
"sha256": "91f9df68c0d3b9c0ace6948145362f303291c5698bb2594bff9bb9c3aabc159c"
},
{
"path": "outputs/treatment-install-20261023/installed-links.json",
"sha256": "bf73f59ecfaccf8d10f45a72c36bd684e1b12abb667a65e0d2cda4dd396eb324"
},
{
"path": "outputs/treatment-install-20261023/installed-scope-summary.json",
"sha256": "fd410fa10ee63b8c09c311530cab78ca4f1f2b961c3d31cc78c40b86bc7af255"
},
{
"path": "outputs/treatment-install-20261023/installed-dossier.txt",
"sha256": "b5577b82ad0bce03131ffddbc365f1d039229549d6b83227ad57baffd1037f07"
},
{
"path": "outputs/treatment-install-20261023/installed-dossier.png",
"sha256": "9a3133a9fb6ca11fc64e0681393d518676bb72bceb75bfc95950466271158509"
}
]
}
@@ -1,51 +1,51 @@
ID legado,Título,Autor,Categoria,Publicado,Modificado,SHA256 conteúdo,URLs observadas,Recurso QA,Situação,Situação anterior à reconciliação 15/10/2026,Recurso ativo,Grupo restante,Imagem atual,Dependência concreta,Próxima ação,Evidência atual
171985,Agronegocio,tempo,Oportunidades,2026-04-06T09:19:07+00:00,2026-04-20T12:41:43+00:00,28383a11d95f13894381357d8f1227b33031d1d3ff74507da4ea8e5ce2470913,https://investemoz.tempo.co.mz/agronegocio,6,publicado,recurso único QA; não é oportunidade,5,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 6, ""sha256"": ""5e3840705cf98226d1f189cf665aaa98cb05da96c645159a1332a1d429d642eb""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62413,15/30 costa do sol com dua na zona de mupulene muito lindo,tempo,Categoria,2017-06-26T11:44:21+00:00,2017-06-26T11:44:21+00:00,c04ae821317ef556c01c3141ce75eeeeb5da7787e0c6849b1b3709b757edb9eb,https://investemoz.tempo.co.mz/1530-costa-do-sol-com-dua-na-zona-de-mupulene-muito-lindo,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/817802064_1_644x461_15-30-costa-do-sol-com-dua-na-zona-de-mupulene-muito-lindo-maputo.jpg | Referência não recolhida: https://www.olx.co.mz/imoveis-casas/terrenos/terrenos-venda/maputo-maputo/?search%5Bprivate_business%5D=private | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173220,Alumínio “Verde”,tempo,Oportunidades,2026-06-16T12:41:02+00:00,2026-06-16T12:41:06+00:00,c61f05a56bb5a5f13d9d5d54dbefd270cf9ac40320917d77aaad76cac56c7b97,https://investemoz.tempo.co.mz/aluminio-verde,,existente por integrar,inventariado; cadastro/versão por reconciliar,,setorial,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Ausente da biblioteca ativa e QA; falta exemplar versionado no manifesto de um novo lote e extensão nominal do importador. | Slug ausente no mapa do leitor; falta ligação ao percurso/dossier fundamentada para este artigo. | Não há setor mestre Alumínio Verde com correspondência exata; fundamentar o agrupamento sem inventar vínculo a projeto ou criar setor automaticamente.,Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
73945,Banco Mundial classifica de proibitivos juros praticados em Moçambique,tempo,Nas Redes,2017-07-31T19:55:24+00:00,2017-07-31T19:55:24+00:00,36ba82b8885b00a4ea2fd9e4b1d8cf304a8e78e591972682f0aa53717f648d19,https://investemoz.tempo.co.mz/banco-mundial-classifica-de-proibitivos-juros-praticados-em-mocambique,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/economia/38-economia/45893-banco-mundial-classifica-de-proibitivos-juros-praticados-em-mocambique.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Banco-Mundial-classifica-de-proibitivos-juros-praticados-em-Mo%C3%A7ambique.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1460805843956666 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173213,Bens de Consumo e Franquias,tempo,Oportunidades,2026-06-16T11:31:06+00:00,2026-06-16T11:32:10+00:00,abd5f36a0c2b89d2989334137aae5d59b3158da38d556013ccd55deaa5069e92,https://investemoz.tempo.co.mz/bens-de-consumo-franquias,29,"Publicado no ativo — setores-fintech-QA-d5b4dc354265 DOC01; importação privada, scan, metadados e publicação separados",inventariado; cadastro/versão por reconciliar,29,publicado,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Entrega concluída; nenhum novo envio ou republicação da imagem. Regulação específica Fintech permanece na matriz.,"Preservar publicação; restante acervo separado, sem repetir aceitação.",outputs/investmoz-fintech-investimento-QA-20261015/RESULTADO-QA.md; PROVA-DELTA-QA.json; HTTP-DELTA-QA.json; PROVA-FINAL-DELTA-QA.json; HTTP-RECUPERACAO-FINAL-QA.json; MANIFESTO-ENTREGA.json | outputs/investmoz-setores-fintech-ativacao-20261015/RELATORIO-ENTREGA.md; PUBLISHED.json; PRESERVACAO-FINAL.json; HTTP-OPERACIONAL.json; FRESH-BACKUP.json; RECIBO-FINAL.json
172223,"Cabo Delgado - Pemba, A capital do turismo costeiro do norte de Moçambique",tempo,Turismo,2026-04-14T10:21:20+00:00,2026-04-14T10:41:05+00:00,c96b2430c44b8edc9cc1d0a7974c23290dd0207cff366fea8326bb28bcab8285,https://investemoz.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique/ | https://investemoz.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique,20,publicado,RecursoQA20; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,20,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173482,Checklist para Contratação e Direitos do Trabalho Doméstico,tempo,Trabalho Doméstico,2026-09-15T12:36:11+00:00,2026-09-15T12:36:14+00:00,309b51ebc53bf840f9014da733bc09f8d162138be4e6e9d5a72670e26cc8e482,https://investemoz.tempo.co.mz/checklist-para-contratacao-e-direitos-do-trabalho-domestico,,existente por integrar,inventariado; cadastro/versão por reconciliar,,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
76474,"Coloque o seu número para o grupo de whatsapp, venda de acessórios de computador",tempo,Categoria,2017-08-08T17:29:51+00:00,2017-08-08T17:29:51+00:00,c8beeb94f936b3bfc69c720770bd84376e9f19b668d96185aaefbada05727010,https://investemoz.tempo.co.mz/coloque-o-seu-numero-para-o-grupo-de-whatsapp-venda-de-acessorios-de-computador,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: https://www.facebook.com/photo.php?fbid=741844132690980&set=gm.1566816196708735&type=3 | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Coloque-o-seu-n%C3%BAmero-para-o-grupo-de-whatsapp-venda-de-acess%C3%B3rios-de-computador.jpg | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/1502293415_298_Coloque-o-seu-n%C3%BAmero-para-o-grupo-de-whatsapp-venda-de-acess%C3%B3rios-de-computador.jpg | Referência não recolhida: https://www.facebook.com/179463555444013/posts/1566816196708735 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173484,Direitos Especiais da Trabalhadora Doméstica no Regulamento,tempo,Trabalho Doméstico,2026-09-15T12:38:08+00:00,2026-09-15T12:38:11+00:00,34c31bb02a3ad0886f12ac2853283de2ffb89a925be3cda73b7ce9385c6caec4,https://investemoz.tempo.co.mz/direitos-especiais-da-trabalhadora-domestica-no-regulamento,,existente por integrar,inventariado; cadastro/versão por reconciliar,,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
171981,Energia & Mineração,tempo,Oportunidades,2026-04-06T09:17:46+00:00,2026-04-30T13:35:32+00:00,e9a95f0c8176eea48a75bd2c5bce69f15308806988fa5203a688af7c40305e9c,https://investemoz.tempo.co.mz/energia-mineracao,7,publicado,RecursoQA7; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,7,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 23, ""sha256"": ""0a2c50d91dad3a6007544634540e0f61b11d3506682a5a19066bae03cdcba490""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173222,Energia e Recursos Naturais,tempo,Oportunidades,2026-06-16T12:47:16+00:00,2026-06-16T12:51:30+00:00,6f8236b0441d61fce5171994ba6b82ffc8b5f2de3e8242de7be29248ed046ad2,https://investemoz.tempo.co.mz/energia-recursos-naturais,,existente por integrar,inventariado; cadastro/versão por reconciliar,,setorial,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Ausente da biblioteca ativa e QA; falta exemplar versionado no manifesto de um novo lote e extensão nominal do importador. | Slug ausente no mapa do leitor; falta ligação ao percurso/dossier fundamentada para este artigo. | Texto distinto de Energia/Mineração já publicado; preservar a ligação declarada /energia-mineracao e delimitar agrupamento, sem fundir os exemplares.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62414,Frelimo e Zanu-PF: Evocados laços de irmandade,tempo,Categoria,2017-06-26T12:03:20+00:00,2017-06-26T12:03:20+00:00,013307965b8fcb349a0d936d844ed12437bda4c90389f8efe958d4ffdb9ce62b,https://investemoz.tempo.co.mz/frelimo-e-zanu-pf-evocados-lacos-de-irmandade,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência indisponível na captura (HTTP 404): https://investemoz.tempo.co.mz/images/ANO-2017/JUNHO/21/GOVERNADOR-in.gif | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62418,Governo dará todo O apoio à PGR no seguimento da auditoria,tempo,Categoria,2017-06-26T12:30:37+00:00,2017-06-26T12:30:45+00:00,65299b6711499e417be778b51c7ab7880616b0ccbf362854370eb775fc54caf3,https://investemoz.tempo.co.mz/governo-dara-todo-o-apoio-a-pgr-no-seguimento-da-auditoria,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/a84c6c1e4d1780f63086a86817521b40 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
75002,Governo pretende construir 100 mil habitações em dois anos,tempo,Nas Redes,2017-08-04T20:01:55+00:00,2017-08-04T20:01:55+00:00,b32ea95039c2af387687738ed601f0947384eb7e19ca4bd282d603c60ca26afa,https://investemoz.tempo.co.mz/governo-pretende-construir-100-mil-habitacoes-em-dois-anos,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/45963-governo-pretende-construir-100-mil-habitacoes-em-dois-anos.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Governo-pretende-construir-100-mil-habita%C3%A7%C3%B5es-em-dois-anos.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1464602060243711 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173478,Guia Prático para Empregadores sobre Contratação e Direitos no Trabalho Doméstico,tempo,Trabalho Doméstico,2026-09-15T12:30:34+00:00,2026-09-15T12:30:37+00:00,8038d8c221ba3cbbc4712304a6b17a6bba8ad946c8bac1a7836d472944aaf930,https://investemoz.tempo.co.mz/guia-pratico-para-empregadores-sobre-contratacao-e-direitos-no-trabalho-domestico,,existente por integrar,inventariado; cadastro/versão por reconciliar,,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173218,Imobiliário,tempo,Oportunidades,2026-06-16T11:34:45+00:00,2026-06-16T11:34:49+00:00,be8b606f087a939bd88a86f4436a0ff18c5fb774a179e6e915d4abb10a95f930,https://investemoz.tempo.co.mz/imobiliario,28,"Publicado no ativo — setores-fintech-QA-d5b4dc354265 DOC01; importação privada, scan, metadados e publicação separados",inventariado; cadastro/versão por reconciliar,28,publicado,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Entrega concluída; nenhum novo envio ou republicação da imagem. Regulação específica Fintech permanece na matriz.,"Preservar publicação; restante acervo separado, sem repetir aceitação.",outputs/investmoz-fintech-investimento-QA-20261015/RESULTADO-QA.md; PROVA-DELTA-QA.json; HTTP-DELTA-QA.json; PROVA-FINAL-DELTA-QA.json; HTTP-RECUPERACAO-FINAL-QA.json; MANIFESTO-ENTREGA.json | outputs/investmoz-setores-fintech-ativacao-20261015/RELATORIO-ENTREGA.md; PUBLISHED.json; PRESERVACAO-FINAL.json; HTTP-OPERACIONAL.json; FRESH-BACKUP.json; RECIBO-FINAL.json
171992,Infraestruturas,tempo,Oportunidades,2026-04-06T09:25:07+00:00,2026-04-30T12:44:37+00:00,9716da16aba58eec173343f0bbd582d62d23a5eee7d20522c080c2da5da8ba66,https://investemoz.tempo.co.mz/infraestruturas,22,publicado,RecursoQA22; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,22,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 26, ""sha256"": ""8e58544e49e719f9c865c91e07ef1030155a9e3d59260e6dc93e9c9e25674e39""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172240,"Maputo Cidade – Plataforma Estratégica de Turismo, Negócios e Investimento Sustentável",tempo,Turismo,2026-04-14T10:58:53+00:00,2026-04-14T11:03:28+00:00,0866639d09c5c0b3b00e8527554065587c793f60df978b66b6a578b3c741bc0d,https://investemoz.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel/ | https://investemoz.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel,16,publicado,RecursoQA16; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,16,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
171994,Mercado Financeiro,tempo,Oportunidades,2026-04-06T09:27:00+00:00,2026-04-17T09:53:42+00:00,e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,https://investemoz.tempo.co.mz/mercado-financeiro,23,vazio — dependência concreta,"RecursoQA23; original/hash/ClamD/replay demonstrados. Corpo vazio na origemHTML/Inertia200;privado,sem página publicada.",,mercado_financeiro,"{""status"": ""capturada mas não publicada no ativo"", ""sha256"": ""417b4a33468f360acef272ce3e4180c1d0a171980befa73ddb9a33de5a9d4c4e""}",Corpo do artigo vazio; imagem não substitui conteúdo.,"Manter pendente e separado, sem bloquear outros lotes.",outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173480,Modelo de Contrato de Trabalho Doméstico para Pequenas Famílias,tempo,Trabalho Doméstico,2026-09-15T12:34:17+00:00,2026-09-15T12:34:19+00:00,5b95ad9010fcbdc1000052f47766ca54409d3e0649211729ced9ef142f5c6db0,https://investemoz.tempo.co.mz/modelo-de-contrato-de-trabalho-domestico-para-pequenas-familias,,existente por integrar,inventariado; cadastro/versão por reconciliar,,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172219,Niassa - O último grande destino virgem do turismo africano,tempo,Turismo,2026-04-14T10:17:14+00:00,2026-04-14T10:43:56+00:00,47d82a59a86c3cbdef04ea065e711e7139d50137e440f03efbd710e3c4220ef7,https://investemoz.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano/ | https://investemoz.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano,21,publicado,RecursoQA21; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,21,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173476,Procedimentos para Rescisão do Contrato de Trabalho Doméstico segundo o Regulamento de Moçambique,tempo,Trabalho Doméstico,2026-09-15T11:39:21+00:00,2026-09-15T11:39:24+00:00,bb634a9021da9d7b46ed105317aa54e4af42a16dab042e0ceb2bdc0d702d1a67,https://investemoz.tempo.co.mz/procedimentos-para-rescisao-do-contrato-de-trabalho-domestico-segundo-o-regulamento-de-mocambique,,existente por integrar,inventariado; cadastro/versão por reconciliar,,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172263,"Província da Zambézia: Onde os Corredores de Moçambique Convergem com a Natureza, a Cultura e o Turismo Sustentável",tempo,Turismo,2026-04-14T14:21:52+00:00,2026-04-14T14:26:27+00:00,9e7ca4e9542135d5969f5c008ce8e29b037cc06a53875cfa7dc3e7452ec47744,https://investemoz.tempo.co.mz/provincia-da-zambezia-onde-os-corredores-de-mocambique-convergem-com-a-natureza-a-cultura-e-o-turismo-sustentavel,11,publicado,RecursoQA11; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,11,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172233,Província de Gaza: Oportunidades mais sólidas e estratégicas de investimento no turismo no sul de Moçambique.,tempo,Turismo,2026-04-14T10:52:30+00:00,2026-04-17T08:28:56+00:00,2bf2df9bd3b7ab8656209a48f4928b5d8bbf2959e77c292fc4c9987e67ba2fea,https://investemoz.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique/ | https://investemoz.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique,18,publicado,RecursoQA18; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,18,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172253,Província de Inhambane: Um Destino Estratégico para Investimento Turístico,tempo,Turismo,2026-04-14T14:15:12+00:00,2026-04-14T14:25:44+00:00,2d1cc729481ce65a5e50898c0eea4a1eb2418273b03fc749009e2ffaaa72fe71,https://investemoz.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico/ | https://investemoz.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico,15,publicado,RecursoQA15; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,15,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172258,"Província de Manica: Onde o Turismo de Montanha, Natureza e Cultura Encontra o Próximo Ciclo de Investimento",tempo,Turismo,2026-04-14T14:18:28+00:00,2026-04-17T10:42:16+00:00,4c8bdd79212ba98fa347733e2f58aef8796b29cc8cb8b649d200bd37435df1c3,https://investemoz.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento/ | https://investemoz.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento,14,publicado,RecursoQA14; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,14,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172237,"Província de Maputo: Natureza, Cultura e Conectividade como Motores de Crescimento Sustentável",tempo,Turismo,2026-04-14T10:55:21+00:00,2026-04-17T08:27:24+00:00,d112d0e535912f4487c2b8d41978e5e663f1db43f1a2cdf170dbbec535f13f27,https://investemoz.tempo.co.mz/provincia-de-maputo-turismo/ | https://investemoz.tempo.co.mz/provincia-de-maputo-turismo,17,publicado,RecursoQA17; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,17,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172230,Província de Nampula - Perfil de investimento no setor do turismo,tempo,Turismo,2026-04-14T10:50:32+00:00,2026-04-14T11:10:23+00:00,d18ccc1e036b3480605462ce7176be75153f6aa988bc9b37d8d395b01e555a7a,https://investemoz.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo/ | https://investemoz.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo,19,publicado,RecursoQA19; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,19,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172256,"Província de Sofala: Onde a Natureza, a História e o Futuro do Turismo Convergem",tempo,Turismo,2026-04-14T14:16:47+00:00,2026-04-14T14:26:17+00:00,12b46a34ba61c6ef0058a6c410ebc17329d897fe4b06a13b4ea3fe3c85a28342,https://investemoz.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem/ | https://investemoz.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem,12,publicado,RecursoQA12; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,12,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172261,"Província de Tete: Onde o Rio Zambeze, a Cultura Viva e a Conservação Comunitária Criam o Futuro do Turismo",tempo,Turismo,2026-04-14T14:19:48+00:00,2026-04-14T14:26:25+00:00,e78fce2931260a7d6f82270897e906feb640f65d084c2d4ffb5ba19303858275,https://investemoz.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo/ | https://investemoz.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo,13,publicado,RecursoQA13; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,13,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
73246,Rofino Licuco preso cinco meses depois de condenado por agressão,tempo,Nas Redes,2017-07-28T20:23:11+00:00,2017-07-28T20:23:11+00:00,806c54948641f4f5933a2ea4a6935dfb7a67202ab227960451d62435880289f6,https://investemoz.tempo.co.mz/rofino-licuco-preso-cinco-meses-depois-de-condenado-por-agressao,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/45870-rofino-licuco-preso-cinco-meses-depois-de-condenado-por-agressao-.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Rofino-Licuco-preso-cinco-meses-depois-de-condenado-por-agress%C3%A3o.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1458270617543522 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
77232,Sete indivíduos mortos a tiro na Matola,tempo,Nas Redes,2017-08-11T20:30:32+00:00,2017-08-11T20:30:32+00:00,e657a97d5d6db7fa0ea6b7d14029ff873ea5d36dfdadda5f8b9bc8f1ef5dd011,https://investemoz.tempo.co.mz/sete-individuos-mortos-a-tiro-na-matola,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/46074-sete-individuos-mortos-a-tiro-na-matola-.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Sete-indiv%C3%ADduos-mortos-a-tiro-na-Matola.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1471269102910340 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62416,Sete pessoas morrem e 150 são resgatados em acidente com eléctrico na Índia,tempo,Categoria,2017-06-26T12:14:48+00:00,2017-06-26T12:14:48+00:00,0fe20a9ca9dd99c58072f43b3dece1de347ef4063ee5f75bcffe8b2f4bf253b5,https://investemoz.tempo.co.mz/sete-pessoas-morrem-e-150-sao-resgatados-em-acidente-com-electrico-na-india,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://www.verdade.co.mz/internacional/62604-sete-pessoas-morrem-e-150-sao-resgatados-em-acidente-com-electrico-na-india | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173209,Fintech,tempo,Oportunidades,2026-06-16T11:24:22+00:00,2026-06-23T10:03:06+00:00,8db9206bf5192b68e7c9bdfb87e9dc9c7063a4b7eca56630685b0e6c294f55ab,https://investemoz.tempo.co.mz/tech-fintech,27,"Publicado no ativo — setores-fintech-QA-d5b4dc354265 DOC01; importação privada, scan, metadados e publicação separados",inventariado; cadastro/versão por reconciliar,27,publicado,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Entrega concluída; nenhum novo envio ou republicação da imagem. Regulação específica Fintech permanece na matriz.,"Preservar publicação; restante acervo separado, sem repetir aceitação.",outputs/investmoz-fintech-investimento-QA-20261015/RESULTADO-QA.md; PROVA-DELTA-QA.json; HTTP-DELTA-QA.json; PROVA-FINAL-DELTA-QA.json; HTTP-RECUPERACAO-FINAL-QA.json; MANIFESTO-ENTREGA.json | outputs/investmoz-setores-fintech-ativacao-20261015/RELATORIO-ENTREGA.md; PUBLISHED.json; PRESERVACAO-FINAL.json; HTTP-OPERACIONAL.json; FRESH-BACKUP.json; RECIBO-FINAL.json
171990,Turismo,tempo,Oportunidades,2026-04-06T09:22:51+00:00,2026-06-16T13:07:59+00:00,604d30a740cc6ed20d24e3447806e733aa4aefb9f339f81d3dc57b644dd19fb7,https://investemoz.tempo.co.mz/turismo,10,publicado,RecursoQA10; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,10,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 25, ""sha256"": ""fe9a2e382e385b35f6e61ae320e199a979678677fd327fb7d4b07d8ce0111c11""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
74364,,tempo,Categoria,2017-07-26T21:20:33+00:00,2017-07-26T21:20:33+00:00,2a7f0a5b09e0136fec9eae357ac8a88a159ead23b25ed7bb735c75997b10ec5e,https://investemoz.tempo.co.mz/74364-2,,dependência concreta — título ausente,inventariado; cadastro/versão por reconciliar,,identidade_incompleta,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/1501695692_133_notitle.jpg | Referência não recolhida: https://www.facebook.com/179463555444013/posts/1552815684775453 | Título vazio no post 74364; corpo com 20 caracteres; identidade editorial a esclarecer sem inventar título.,Preservar exemplar e referências; delimitar recuperação editorial antes de integrar.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62929,Daúde Razaque já não é treinador da Liga Desportiva de Maputo,tempo,Nas Redes,2017-06-27T16:41:43+00:00,2017-06-27T16:41:43+00:00,c3b57ea685290bfb01c50403bc83db98cf1a42ad51c04d5b8d288c99b8c0a808,https://investemoz.tempo.co.mz/daude-razaque-ja-nao-e-treinador-da-liga-desportiva-de-maputo,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/desporto/74-desporto/45358-daude-razaque-ja-nao-e-treinador-da-liga-desportiva-de-maputo.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Da%C3%BAde-Razaque-j%C3%A1-n%C3%A3o-%C3%A9-treinador-da-Liga-Desportiva-de-Maputo.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1425759480794636 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62649,Daviz Simango não se incomoda com possível sucessão na Beira,tempo,Nas Redes,2017-06-26T19:38:09+00:00,2017-06-26T19:38:09+00:00,090e5f41ed11c8e680d2e30ea5c8310b7c6b165ddec318a8b0cf94927f78da74,https://investemoz.tempo.co.mz/daviz-simango-nao-se-incomoda-com-possivel-sucessao-na-beira,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/politica/63-politica/45340-daviz-simango-nao-se-incomoda-com-possivel-sucessao-na-beira-.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Daviz-Simango-n%C3%A3o-se-incomoda-com-poss%C3%ADvel-sucess%C3%A3o-na-Beira.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1424683617568889 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173470,Direitos e Deveres do Trabalhador Doméstico,tempo,Trabalho Doméstico,2026-09-15T11:28:25+00:00,2026-09-15T11:28:28+00:00,f69fd6fd8509913f2d79df0267c469dc7a9d0d30420ce8428749d53ba21446ab,https://investemoz.tempo.co.mz/direitos-e-deveres-do-trabalhador-domestico,,existente por integrar,inventariado; cadastro/versão por reconciliar,,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172679,Energia,tempo,Featured,2026-04-30T13:03:57+00:00,2026-04-30T13:28:48+00:00,e2a7eb4be0a2cf4139737f6428c147ac35de99899478ce79195dc5e1b0a960f3,https://investemoz.tempo.co.mz/energia | https://investemoz.tempo.co.mz/energia/,8,publicado,RecursoQA8; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,8,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62739,"“Exigimos responsabilização dos agentes do crime”, Daviz Simango",tempo,Nas Redes,2017-06-26T23:13:36+00:00,2017-06-26T23:13:36+00:00,e37ea18176246dab529d2ecec7734f0fb2b0a54cadc507fbd789033d5b8e5b93,https://investemoz.tempo.co.mz/exigimos-responsabilizacao-dos-agentes-do-crime-daviz-simango-2,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/politica/63-politica/45345-exigimos-responsabilizacao-dos-agentes-do-crime-daviz-simango.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/%E2%80%9CExigimos-responsabiliza%C3%A7%C3%A3o-dos-agentes-do-crime%E2%80%9D-Daviz-Simango.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1424873994216518 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62854,Governo e parceiros reforçam mecanismos de controlo de HIV/Sida,tempo,Nas Redes,2017-06-27T11:07:53+00:00,2017-06-27T11:07:53+00:00,0073816b1fa4bd50ec6417b07272da06565230d06a19079ca76cb006c882d2f1,https://investemoz.tempo.co.mz/governo-e-parceiros-reforcam-mecanismos-de-controlo-de-hivsida,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/45352-governo-e-parceiros-reforcam-mecanismos-de-controlo-de-hivsida.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Governo-e-parceiros-refor%C3%A7am-mecanismos-de-controlo-de-HIVSida.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1425451974158720 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
63128,Graça Machel diz que desnutrição é um problema de desenvolvimento,tempo,Nas Redes,2017-06-27T22:01:59+00:00,2017-06-27T22:01:59+00:00,711f3707e3a0d72ec919216fc50823de9060349104ee98fe0a6b20d6684b6031,https://investemoz.tempo.co.mz/graca-machel-diz-que-desnutricao-e-um-problema-de-desenvolvimento,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/45368-graca-machel-diz-que-desnutricao-e-um-problema-de-desenvolvimento.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Gra%C3%A7a-Machel-diz-que-desnutri%C3%A7%C3%A3o-%C3%A9-um-problema-de-desenvolvimento.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1426040144099903 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
63181,In dependência,tempo,Nas Redes,2017-06-27T21:38:06+00:00,2017-06-27T21:38:06+00:00,b0d87c0d58d6d1343d102cd3141bda1215c5afd54f3780a1c6c262d786dbfe27,https://investemoz.tempo.co.mz/in-dependencia,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://opais.sapo.mz/index.php/opiniao/182-helder-faife/45364-in-dependencia.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/In-depend%C3%AAncia.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1426022220768362 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
63146,"Kroll não descobriu como foram gastos 2 biliões de dólares; Governo de Nyusi não apoiou a Auditoria; criação da Proindicus, EMATUM e MAM não foi para proteção da Zona Económica Exclusiva",tempo,Nas Redes,2017-06-27T19:30:01+00:00,2017-06-27T19:30:01+00:00,4ccbad5c92ddc3884f58181a4f1ed3c1d3d6a59f005bfd99b197a6fc1fc3c280,https://investemoz.tempo.co.mz/kroll-nao-descobriu-como-foram-gastos-2-bilioes-de-dolares-governo-de-nyusi-nao-apoiou-a-auditoria-criacao-da-proindicus-ematum-e-mam-nao-foi-para-protecao-da-zona-economica-exclusiva-2,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://www.verdade.co.mz/tema-de-fundo/35/62615 | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Kroll-n%C3%A3o-descobriu-como-foram-gastos-2-bili%C3%B5es-de-d%C3%B3lares-Governo-de-Nyusi-n%C3%A3o-apoiou-a-Auditoria-cria%C3%A7%C3%A3o-da-Proindicus-EMATUM-e-MAM-n%C3%A3o-foi-para-prote%C3%A7%C3%A3o-da-Zona-Econ%C3%B3mica-Exclusiva.jpg | Referência não recolhida: https://www.facebook.com/113487338672967/posts/1492451004109920 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
172674,Mineração,tempo,Featured,2026-04-30T13:12:09+00:00,2026-04-30T13:30:13+00:00,8d3acc292ed806bc4cc7bcb6a3a6a5759f30b610766a5ef4b3d6a0b7c56d5861,https://investemoz.tempo.co.mz/mineracao,9,publicado,RecursoQA9; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,9,publicados,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",,Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
62688,O Instituto Nacional de Meteorologia prevê o seguinte estado do tempo para esta ...,tempo,Nas Redes,2017-06-26T18:21:18+00:00,2017-06-26T18:21:18+00:00,396fe2575641622015505eb11d4c7152b6e3e1b741966f14c0cd1673fd455fd3,https://investemoz.tempo.co.mz/o-instituto-nacional-de-meteorologia-preve-o-seguinte-estado-do-tempo-para-esta-29,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://www.verdade.co.mz/newsflash/62607 | Referência não recolhida: https://www.facebook.com/113487338672967/posts/1491493337539020 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173474,Sanções Disciplinares Aplicáveis a Trabalhadores Domésticos segundo o Regulamento de Moçambique,tempo,Trabalho Doméstico,2026-09-15T11:37:04+00:00,2026-09-15T11:37:06+00:00,e09a0fe71397e2b060ff8435111117aa487b476a85ec522fffcdf9e23a6cdac2,https://investemoz.tempo.co.mz/sancoes-disciplinares-aplicaveis-a-trabalhadores-domesticos-segundo-o-regulamento-de-mocambique,,existente por integrar,inventariado; cadastro/versão por reconciliar,,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
63248,Dois alunos da 9ª classe caíram nas mãos da Polícia da República de Moçambique (...,tempo,Nas Redes,2017-06-28T11:30:01+00:00,2017-06-28T11:30:01+00:00,2220ca2c07d10f10b2bd6fd0f103ee733b83570d436c751c449c85b42df46a13,https://investemoz.tempo.co.mz/dois-alunos-da-9a-classe-cairam-nas-maos-da-policia-da-republica-de-mocambique,,existente por integrar,inventariado; cadastro/versão por reconciliar,,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Referência não recolhida: http://www.verdade.co.mz/newsflash/62625 | Referência não recolhida: https://www.facebook.com/113487338672967/posts/1493133914041629 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
173463,Regulamento do Trabalho Doméstico,tempo,Trabalho Doméstico,2026-09-15T10:56:07+00:00,2026-09-15T12:41:44+00:00,eb43b6c1f097631250f0b36d3d01b4d106db2fcae07a56bb154819143fff11e9,https://investemoz.tempo.co.mz/regulamento-do-trabalho-domestico,,existente por integrar,inventariado; cadastro/versão por reconciliar,,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}","Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites.",Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação.,outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY)
ID legado,Título,Autor,Categoria,Publicado,Modificado,SHA256 conteúdo,URLs observadas,Recurso QA,Situação,Situação anterior à reconciliação 15/10/2026,Recurso ativo,Grupo restante,Imagem atual,Dependência concreta,Próxima ação,Evidência atual,Endereço no novo portal,Estado actual consolidado,Pendência actual,Conferência HTTP UTC,Grupo de preparação QA
171985,Agronegocio,tempo,Oportunidades,2026-04-06T09:19:07+00:00,2026-04-20T12:41:43+00:00,28383a11d95f13894381357d8f1227b33031d1d3ff74507da4ea8e5ce2470913,https://investemoz.tempo.co.mz/agronegocio,6,publicado,recurso único QA; não é oportunidade,13,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/12/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/agronegocio,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
62413,15/30 costa do sol com dua na zona de mupulene muito lindo,tempo,Categoria,2017-06-26T11:44:21+00:00,2017-06-26T11:44:21+00:00,c04ae821317ef556c01c3141ce75eeeeb5da7787e0c6849b1b3709b757edb9eb,https://investemoz.tempo.co.mz/1530-costa-do-sol-com-dua-na-zona-de-mupulene-muito-lindo,43,existente por integrar,inventariado; cadastro/versão por reconciliar,32,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Rever/corrigir a nota de demonstração QA antes de publicar.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/32/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Rever/corrigir a nota de demonstração QA antes de publicar.,,historico_noticioso
173220,Alumínio “Verde”,tempo,Oportunidades,2026-06-16T12:41:02+00:00,2026-06-16T12:41:06+00:00,c61f05a56bb5a5f13d9d5d54dbefd270cf9ac40320917d77aaad76cac56c7b97,https://investemoz.tempo.co.mz/aluminio-verde,51,existente por integrar,inventariado; cadastro/versão por reconciliar,50,setorial,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/50/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,setorial
73945,Banco Mundial classifica de proibitivos juros praticados em Moçambique,tempo,Nas Redes,2017-07-31T19:55:24+00:00,2017-07-31T19:55:24+00:00,36ba82b8885b00a4ea2fd9e4b1d8cf304a8e78e591972682f0aa53717f648d19,https://investemoz.tempo.co.mz/banco-mundial-classifica-de-proibitivos-juros-praticados-em-mocambique,44,existente por integrar,inventariado; cadastro/versão por reconciliar,33,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/33/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
173213,Bens de Consumo e Franquias,tempo,Oportunidades,2026-06-16T11:31:06+00:00,2026-06-16T11:32:10+00:00,abd5f36a0c2b89d2989334137aae5d59b3158da38d556013ccd55deaa5069e92,https://investemoz.tempo.co.mz/bens-de-consumo-franquias,29,publicado,inventariado; cadastro/versão por reconciliar,29,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/bens-de-consumo-franquias,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172223,"Cabo Delgado - Pemba, A capital do turismo costeiro do norte de Moçambique",tempo,Turismo,2026-04-14T10:21:20+00:00,2026-04-14T10:41:05+00:00,c96b2430c44b8edc9cc1d0a7974c23290dd0207cff366fea8326bb28bcab8285,https://investemoz.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique/ | https://investemoz.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique,20,publicado,RecursoQA20; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,25,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
173482,Checklist para Contratação e Direitos do Trabalho Doméstico,tempo,Trabalho Doméstico,2026-09-15T12:36:11+00:00,2026-09-15T12:36:14+00:00,309b51ebc53bf840f9014da733bc09f8d162138be4e6e9d5a72670e26cc8e482,https://investemoz.tempo.co.mz/checklist-para-contratacao-e-direitos-do-trabalho-domestico,52,existente por integrar,inventariado; cadastro/versão por reconciliar,52,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/52/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,trabalho_domestico
76474,"Coloque o seu número para o grupo de whatsapp, venda de acessórios de computador",tempo,Categoria,2017-08-08T17:29:51+00:00,2017-08-08T17:29:51+00:00,c8beeb94f936b3bfc69c720770bd84376e9f19b668d96185aaefbada05727010,https://investemoz.tempo.co.mz/coloque-o-seu-numero-para-o-grupo-de-whatsapp-venda-de-acessorios-de-computador,45,existente por integrar,inventariado; cadastro/versão por reconciliar,34,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/34/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
173484,Direitos Especiais da Trabalhadora Doméstica no Regulamento,tempo,Trabalho Doméstico,2026-09-15T12:38:08+00:00,2026-09-15T12:38:11+00:00,34c31bb02a3ad0886f12ac2853283de2ffb89a925be3cda73b7ce9385c6caec4,https://investemoz.tempo.co.mz/direitos-especiais-da-trabalhadora-domestica-no-regulamento,64,existente por integrar,inventariado; cadastro/versão por reconciliar,53,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/53/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,trabalho_domestico
171981,Energia & Mineração,tempo,Oportunidades,2026-04-06T09:17:46+00:00,2026-04-30T13:35:32+00:00,e9a95f0c8176eea48a75bd2c5bce69f15308806988fa5203a688af7c40305e9c,https://investemoz.tempo.co.mz/energia-mineracao,7,publicado,RecursoQA7; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,18,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/14/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/energia-mineracao,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
173222,Energia e Recursos Naturais,tempo,Oportunidades,2026-06-16T12:47:16+00:00,2026-06-16T12:51:30+00:00,6f8236b0441d61fce5171994ba6b82ffc8b5f2de3e8242de7be29248ed046ad2,https://investemoz.tempo.co.mz/energia-recursos-naturais,63,existente por integrar,inventariado; cadastro/versão por reconciliar,51,setorial,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/51/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,setorial
62414,Frelimo e Zanu-PF: Evocados laços de irmandade,tempo,Categoria,2017-06-26T12:03:20+00:00,2017-06-26T12:03:20+00:00,013307965b8fcb349a0d936d844ed12437bda4c90389f8efe958d4ffdb9ce62b,https://investemoz.tempo.co.mz/frelimo-e-zanu-pf-evocados-lacos-de-irmandade,53,existente por integrar,inventariado; cadastro/versão por reconciliar,35,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/35/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
62418,Governo dará todo O apoio à PGR no seguimento da auditoria,tempo,Categoria,2017-06-26T12:30:37+00:00,2017-06-26T12:30:45+00:00,65299b6711499e417be778b51c7ab7880616b0ccbf362854370eb775fc54caf3,https://investemoz.tempo.co.mz/governo-dara-todo-o-apoio-a-pgr-no-seguimento-da-auditoria,54,existente por integrar,inventariado; cadastro/versão por reconciliar,36,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/36/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
75002,Governo pretende construir 100 mil habitações em dois anos,tempo,Nas Redes,2017-08-04T20:01:55+00:00,2017-08-04T20:01:55+00:00,b32ea95039c2af387687738ed601f0947384eb7e19ca4bd282d603c60ca26afa,https://investemoz.tempo.co.mz/governo-pretende-construir-100-mil-habitacoes-em-dois-anos,46,existente por integrar,inventariado; cadastro/versão por reconciliar,37,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/37/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
173478,Guia Prático para Empregadores sobre Contratação e Direitos no Trabalho Doméstico,tempo,Trabalho Doméstico,2026-09-15T12:30:34+00:00,2026-09-15T12:30:37+00:00,8038d8c221ba3cbbc4712304a6b17a6bba8ad946c8bac1a7836d472944aaf930,https://investemoz.tempo.co.mz/guia-pratico-para-empregadores-sobre-contratacao-e-direitos-no-trabalho-domestico,65,existente por integrar,inventariado; cadastro/versão por reconciliar,54,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/54/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,trabalho_domestico
173218,Imobiliário,tempo,Oportunidades,2026-06-16T11:34:45+00:00,2026-06-16T11:34:49+00:00,be8b606f087a939bd88a86f4436a0ff18c5fb774a179e6e915d4abb10a95f930,https://investemoz.tempo.co.mz/imobiliario,28,publicado,inventariado; cadastro/versão por reconciliar,7,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/imobiliario,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
171992,Infraestruturas,tempo,Oportunidades,2026-04-06T09:25:07+00:00,2026-04-30T12:44:37+00:00,9716da16aba58eec173343f0bbd582d62d23a5eee7d20522c080c2da5da8ba66,https://investemoz.tempo.co.mz/infraestruturas,22,publicado,RecursoQA22; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,17,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/16/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/infraestruturas,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172240,"Maputo Cidade – Plataforma Estratégica de Turismo, Negócios e Investimento Sustentável",tempo,Turismo,2026-04-14T10:58:53+00:00,2026-04-14T11:03:28+00:00,0866639d09c5c0b3b00e8527554065587c793f60df978b66b6a578b3c741bc0d,https://investemoz.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel/ | https://investemoz.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel,16,publicado,RecursoQA16; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,21,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
171994,Mercado Financeiro,tempo,Oportunidades,2026-04-06T09:27:00+00:00,2026-04-17T09:53:42+00:00,e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,https://investemoz.tempo.co.mz/mercado-financeiro,23,vazio — dependência concreta,"RecursoQA23; original/hash/ClamD/replay demonstrados. Corpo vazio na origemHTML/Inertia200;privado,sem página publicada.",,mercado_financeiro,"{""status"": ""capturada mas não publicada no ativo"", ""sha256"": ""417b4a33468f360acef272ce3e4180c1d0a171980befa73ddb9a33de5a9d4c4e""}",Corpo do artigo vazio; imagem não substitui conteúdo.,"Manter pendente e separado, sem bloquear outros lotes.",outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,,Original vazio,Fornecer corpo editorial pela Tempo; imagem e Fintech não substituem o original. Sem novo endereço público.,,
173480,Modelo de Contrato de Trabalho Doméstico para Pequenas Famílias,tempo,Trabalho Doméstico,2026-09-15T12:34:17+00:00,2026-09-15T12:34:19+00:00,5b95ad9010fcbdc1000052f47766ca54409d3e0649211729ced9ef142f5c6db0,https://investemoz.tempo.co.mz/modelo-de-contrato-de-trabalho-domestico-para-pequenas-familias,66,existente por integrar,inventariado; cadastro/versão por reconciliar,55,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/55/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,trabalho_domestico
172219,Niassa - O último grande destino virgem do turismo africano,tempo,Turismo,2026-04-14T10:17:14+00:00,2026-04-14T10:43:56+00:00,47d82a59a86c3cbdef04ea065e711e7139d50137e440f03efbd710e3c4220ef7,https://investemoz.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano/ | https://investemoz.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano,21,publicado,RecursoQA21; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,26,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
173476,Procedimentos para Rescisão do Contrato de Trabalho Doméstico segundo o Regulamento de Moçambique,tempo,Trabalho Doméstico,2026-09-15T11:39:21+00:00,2026-09-15T11:39:24+00:00,bb634a9021da9d7b46ed105317aa54e4af42a16dab042e0ceb2bdc0d702d1a67,https://investemoz.tempo.co.mz/procedimentos-para-rescisao-do-contrato-de-trabalho-domestico-segundo-o-regulamento-de-mocambique,67,existente por integrar,inventariado; cadastro/versão por reconciliar,56,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/56/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,trabalho_domestico
172263,"Província da Zambézia: Onde os Corredores de Moçambique Convergem com a Natureza, a Cultura e o Turismo Sustentável",tempo,Turismo,2026-04-14T14:21:52+00:00,2026-04-14T14:26:27+00:00,9e7ca4e9542135d5969f5c008ce8e29b037cc06a53875cfa7dc3e7452ec47744,https://investemoz.tempo.co.mz/provincia-da-zambezia-onde-os-corredores-de-mocambique-convergem-com-a-natureza-a-cultura-e-o-turismo-sustentavel,11,publicado,RecursoQA11; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,3,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/provincia-da-zambezia-onde-os-corredores-de-mocambique-convergem-com-a-natureza-a-cultura-e-o-turismo-sustentavel,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172233,Província de Gaza: Oportunidades mais sólidas e estratégicas de investimento no turismo no sul de Moçambique.,tempo,Turismo,2026-04-14T10:52:30+00:00,2026-04-17T08:28:56+00:00,2bf2df9bd3b7ab8656209a48f4928b5d8bbf2959e77c292fc4c9987e67ba2fea,https://investemoz.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique/ | https://investemoz.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique,18,publicado,RecursoQA18; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,23,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172253,Província de Inhambane: Um Destino Estratégico para Investimento Turístico,tempo,Turismo,2026-04-14T14:15:12+00:00,2026-04-14T14:25:44+00:00,2d1cc729481ce65a5e50898c0eea4a1eb2418273b03fc749009e2ffaaa72fe71,https://investemoz.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico/ | https://investemoz.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico,15,publicado,RecursoQA15; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,6,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172258,"Província de Manica: Onde o Turismo de Montanha, Natureza e Cultura Encontra o Próximo Ciclo de Investimento",tempo,Turismo,2026-04-14T14:18:28+00:00,2026-04-17T10:42:16+00:00,4c8bdd79212ba98fa347733e2f58aef8796b29cc8cb8b649d200bd37435df1c3,https://investemoz.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento/ | https://investemoz.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento,14,publicado,RecursoQA14; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,20,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172237,"Província de Maputo: Natureza, Cultura e Conectividade como Motores de Crescimento Sustentável",tempo,Turismo,2026-04-14T10:55:21+00:00,2026-04-17T08:27:24+00:00,d112d0e535912f4487c2b8d41978e5e663f1db43f1a2cdf170dbbec535f13f27,https://investemoz.tempo.co.mz/provincia-de-maputo-turismo/ | https://investemoz.tempo.co.mz/provincia-de-maputo-turismo,17,publicado,RecursoQA17; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,22,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/provincia-de-maputo-turismo,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172230,Província de Nampula - Perfil de investimento no setor do turismo,tempo,Turismo,2026-04-14T10:50:32+00:00,2026-04-14T11:10:23+00:00,d18ccc1e036b3480605462ce7176be75153f6aa988bc9b37d8d395b01e555a7a,https://investemoz.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo/ | https://investemoz.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo,19,publicado,RecursoQA19; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,24,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172256,"Província de Sofala: Onde a Natureza, a História e o Futuro do Turismo Convergem",tempo,Turismo,2026-04-14T14:16:47+00:00,2026-04-14T14:26:17+00:00,12b46a34ba61c6ef0058a6c410ebc17329d897fe4b06a13b4ea3fe3c85a28342,https://investemoz.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem/ | https://investemoz.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem,12,publicado,RecursoQA12; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,4,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
172261,"Província de Tete: Onde o Rio Zambeze, a Cultura Viva e a Conservação Comunitária Criam o Futuro do Turismo",tempo,Turismo,2026-04-14T14:19:48+00:00,2026-04-14T14:26:25+00:00,e78fce2931260a7d6f82270897e906feb640f65d084c2d4ffb5ba19303858275,https://investemoz.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo/ | https://investemoz.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo,13,publicado,RecursoQA13; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,5,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
73246,Rofino Licuco preso cinco meses depois de condenado por agressão,tempo,Nas Redes,2017-07-28T20:23:11+00:00,2017-07-28T20:23:11+00:00,806c54948641f4f5933a2ea4a6935dfb7a67202ab227960451d62435880289f6,https://investemoz.tempo.co.mz/rofino-licuco-preso-cinco-meses-depois-de-condenado-por-agressao,47,existente por integrar,inventariado; cadastro/versão por reconciliar,38,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/38/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
77232,Sete indivíduos mortos a tiro na Matola,tempo,Nas Redes,2017-08-11T20:30:32+00:00,2017-08-11T20:30:32+00:00,e657a97d5d6db7fa0ea6b7d14029ff873ea5d36dfdadda5f8b9bc8f1ef5dd011,https://investemoz.tempo.co.mz/sete-individuos-mortos-a-tiro-na-matola,48,existente por integrar,inventariado; cadastro/versão por reconciliar,39,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/39/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
62416,Sete pessoas morrem e 150 são resgatados em acidente com eléctrico na Índia,tempo,Categoria,2017-06-26T12:14:48+00:00,2017-06-26T12:14:48+00:00,0fe20a9ca9dd99c58072f43b3dece1de347ef4063ee5f75bcffe8b2f4bf253b5,https://investemoz.tempo.co.mz/sete-pessoas-morrem-e-150-sao-resgatados-em-acidente-com-electrico-na-india,55,existente por integrar,inventariado; cadastro/versão por reconciliar,40,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/40/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
173209,Fintech,tempo,Oportunidades,2026-06-16T11:24:22+00:00,2026-06-23T10:03:06+00:00,8db9206bf5192b68e7c9bdfb87e9dc9c7063a4b7eca56630685b0e6c294f55ab,https://investemoz.tempo.co.mz/tech-fintech,27,publicado,inventariado; cadastro/versão por reconciliar,28,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/tech-fintech,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
171990,Turismo,tempo,Oportunidades,2026-04-06T09:22:51+00:00,2026-06-16T13:07:59+00:00,604d30a740cc6ed20d24e3447806e733aa4aefb9f339f81d3dc57b644dd19fb7,https://investemoz.tempo.co.mz/turismo,10,publicado,RecursoQA10; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,19,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/15/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/turismo,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
74364,,tempo,Categoria,2017-07-26T21:20:33+00:00,2017-07-26T21:20:33+00:00,2a7f0a5b09e0136fec9eae357ac8a88a159ead23b25ed7bb735c75997b10ec5e,https://investemoz.tempo.co.mz/74364-2,,dependência concreta — título ausente,inventariado; cadastro/versão por reconciliar,,identidade_incompleta,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/1501695692_133_notitle.jpg | Referência não recolhida: https://www.facebook.com/179463555444013/posts/1552815684775453 | Título vazio no post 74364; corpo com 20 caracteres; identidade editorial a esclarecer sem inventar título.,Preservar exemplar e referências; delimitar recuperação editorial antes de integrar.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,,Original sem título,Identificação editorial pela Tempo para o post 74364; corpo com 21 caracteres úteis preservado. Sem título ou endereço novo inventado.,,
62929,Daúde Razaque já não é treinador da Liga Desportiva de Maputo,tempo,Nas Redes,2017-06-27T16:41:43+00:00,2017-06-27T16:41:43+00:00,c3b57ea685290bfb01c50403bc83db98cf1a42ad51c04d5b8d288c99b8c0a808,https://investemoz.tempo.co.mz/daude-razaque-ja-nao-e-treinador-da-liga-desportiva-de-maputo,49,existente por integrar,inventariado; cadastro/versão por reconciliar,41,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/41/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
62649,Daviz Simango não se incomoda com possível sucessão na Beira,tempo,Nas Redes,2017-06-26T19:38:09+00:00,2017-06-26T19:38:09+00:00,090e5f41ed11c8e680d2e30ea5c8310b7c6b165ddec318a8b0cf94927f78da74,https://investemoz.tempo.co.mz/daviz-simango-nao-se-incomoda-com-possivel-sucessao-na-beira,56,existente por integrar,inventariado; cadastro/versão por reconciliar,42,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/42/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
173470,Direitos e Deveres do Trabalhador Doméstico,tempo,Trabalho Doméstico,2026-09-15T11:28:25+00:00,2026-09-15T11:28:28+00:00,f69fd6fd8509913f2d79df0267c469dc7a9d0d30420ce8428749d53ba21446ab,https://investemoz.tempo.co.mz/direitos-e-deveres-do-trabalhador-domestico,68,existente por integrar,inventariado; cadastro/versão por reconciliar,57,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/57/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,trabalho_domestico
172679,Energia,tempo,Featured,2026-04-30T13:03:57+00:00,2026-04-30T13:28:48+00:00,e2a7eb4be0a2cf4139737f6428c147ac35de99899478ce79195dc5e1b0a960f3,https://investemoz.tempo.co.mz/energia | https://investemoz.tempo.co.mz/energia/,8,publicado,RecursoQA8; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,1,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/energia,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
62739,"“Exigimos responsabilização dos agentes do crime”, Daviz Simango",tempo,Nas Redes,2017-06-26T23:13:36+00:00,2017-06-26T23:13:36+00:00,e37ea18176246dab529d2ecec7734f0fb2b0a54cadc507fbd789033d5b8e5b93,https://investemoz.tempo.co.mz/exigimos-responsabilizacao-dos-agentes-do-crime-daviz-simango-2,57,existente por integrar,inventariado; cadastro/versão por reconciliar,43,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/43/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
62854,Governo e parceiros reforçam mecanismos de controlo de HIV/Sida,tempo,Nas Redes,2017-06-27T11:07:53+00:00,2017-06-27T11:07:53+00:00,0073816b1fa4bd50ec6417b07272da06565230d06a19079ca76cb006c882d2f1,https://investemoz.tempo.co.mz/governo-e-parceiros-reforcam-mecanismos-de-controlo-de-hivsida,58,existente por integrar,inventariado; cadastro/versão por reconciliar,44,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/44/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
63128,Graça Machel diz que desnutrição é um problema de desenvolvimento,tempo,Nas Redes,2017-06-27T22:01:59+00:00,2017-06-27T22:01:59+00:00,711f3707e3a0d72ec919216fc50823de9060349104ee98fe0a6b20d6684b6031,https://investemoz.tempo.co.mz/graca-machel-diz-que-desnutricao-e-um-problema-de-desenvolvimento,59,existente por integrar,inventariado; cadastro/versão por reconciliar,45,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/45/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
63181,In dependência,tempo,Nas Redes,2017-06-27T21:38:06+00:00,2017-06-27T21:38:06+00:00,b0d87c0d58d6d1343d102cd3141bda1215c5afd54f3780a1c6c262d786dbfe27,https://investemoz.tempo.co.mz/in-dependencia,60,existente por integrar,inventariado; cadastro/versão por reconciliar,46,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/46/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
63146,"Kroll não descobriu como foram gastos 2 biliões de dólares; Governo de Nyusi não apoiou a Auditoria; criação da Proindicus, EMATUM e MAM não foi para proteção da Zona Económica Exclusiva",tempo,Nas Redes,2017-06-27T19:30:01+00:00,2017-06-27T19:30:01+00:00,4ccbad5c92ddc3884f58181a4f1ed3c1d3d6a59f005bfd99b197a6fc1fc3c280,https://investemoz.tempo.co.mz/kroll-nao-descobriu-como-foram-gastos-2-bilioes-de-dolares-governo-de-nyusi-nao-apoiou-a-auditoria-criacao-da-proindicus-ematum-e-mam-nao-foi-para-protecao-da-zona-economica-exclusiva-2,61,existente por integrar,inventariado; cadastro/versão por reconciliar,47,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/47/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
172674,Mineração,tempo,Featured,2026-04-30T13:12:09+00:00,2026-04-30T13:30:13+00:00,8d3acc292ed806bc4cc7bcb6a3a6a5759f30b610766a5ef4b3d6a0b7c56d5861,https://investemoz.tempo.co.mz/mineracao,9,publicado,RecursoQA9; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria.,2,publicados,"{""scope"": ""homologação"", ""href"": ""/documentos/27/view"", ""status"": ""referência na ficha; binário não reensaiado""}",Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente.,Preservar a publicação; gerir edições e versões pela biblioteca existente.,outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado,https://teste-investidor.tempo.co.mz/mineracao,Publicado; identidade e conteúdo consultáveis na homologação,Não foi feita aceitação visual ou conferência pela Tempo nesta passagem.,2026-10-22T21:05:03.369388+00:00,
62688,O Instituto Nacional de Meteorologia prevê o seguinte estado do tempo para esta ...,tempo,Nas Redes,2017-06-26T18:21:18+00:00,2017-06-26T18:21:18+00:00,396fe2575641622015505eb11d4c7152b6e3e1b741966f14c0cd1673fd455fd3,https://investemoz.tempo.co.mz/o-instituto-nacional-de-meteorologia-preve-o-seguinte-estado-do-tempo-para-esta-29,62,existente por integrar,inventariado; cadastro/versão por reconciliar,48,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/48/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
173474,Sanções Disciplinares Aplicáveis a Trabalhadores Domésticos segundo o Regulamento de Moçambique,tempo,Trabalho Doméstico,2026-09-15T11:37:04+00:00,2026-09-15T11:37:06+00:00,e09a0fe71397e2b060ff8435111117aa487b476a85ec522fffcdf9e23a6cdac2,https://investemoz.tempo.co.mz/sancoes-disciplinares-aplicaveis-a-trabalhadores-domesticos-segundo-o-regulamento-de-mocambique,69,existente por integrar,inventariado; cadastro/versão por reconciliar,58,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/58/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,trabalho_domestico
63248,Dois alunos da 9ª classe caíram nas mãos da Polícia da República de Moçambique (...,tempo,Nas Redes,2017-06-28T11:30:01+00:00,2017-06-28T11:30:01+00:00,2220ca2c07d10f10b2bd6fd0f103ee733b83570d436c751c449c85b42df46a13,https://investemoz.tempo.co.mz/dois-alunos-da-9a-classe-cairam-nas-maos-da-policia-da-republica-de-mocambique,50,existente por integrar,inventariado; cadastro/versão por reconciliar,49,historico_noticioso,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/49/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,historico_noticioso
173463,Regulamento do Trabalho Doméstico,tempo,Trabalho Doméstico,2026-09-15T10:56:07+00:00,2026-09-15T12:41:44+00:00,eb43b6c1f097631250f0b36d3d01b4d106db2fcae07a56bb154819143fff11e9,https://investemoz.tempo.co.mz/regulamento-do-trabalho-domestico,70,existente por integrar,inventariado; cadastro/versão por reconciliar,59,trabalho_domestico,"{""status"": ""imagem já publicada; reutilizar"", ""resource_id"": 24, ""sha256"": ""28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa""}",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,"Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos.",docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas,https://teste-investidor.tempo.co.mz/management/portal-services/resources/59/article/preview,"Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos",Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas.,,trabalho_domestico
1 ID legado Título Autor Categoria Publicado Modificado SHA256 conteúdo URLs observadas Recurso QA Situação Situação anterior à reconciliação 15/10/2026 Recurso ativo Grupo restante Imagem atual Dependência concreta Próxima ação Evidência atual Endereço no novo portal Estado actual consolidado Pendência actual Conferência HTTP UTC Grupo de preparação QA
2 171985 Agronegocio tempo Oportunidades 2026-04-06T09:19:07+00:00 2026-04-20T12:41:43+00:00 28383a11d95f13894381357d8f1227b33031d1d3ff74507da4ea8e5ce2470913 https://investemoz.tempo.co.mz/agronegocio 6 publicado recurso único QA; não é oportunidade 5 13 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 6, "sha256": "5e3840705cf98226d1f189cf665aaa98cb05da96c645159a1332a1d429d642eb"} {"scope": "homologação", "href": "/documentos/12/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/agronegocio Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
3 62413 15/30 costa do sol com dua na zona de mupulene muito lindo tempo Categoria 2017-06-26T11:44:21+00:00 2017-06-26T11:44:21+00:00 c04ae821317ef556c01c3141ce75eeeeb5da7787e0c6849b1b3709b757edb9eb https://investemoz.tempo.co.mz/1530-costa-do-sol-com-dua-na-zona-de-mupulene-muito-lindo 43 existente por integrar inventariado; cadastro/versão por reconciliar 32 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/817802064_1_644x461_15-30-costa-do-sol-com-dua-na-zona-de-mupulene-muito-lindo-maputo.jpg | Referência não recolhida: https://www.olx.co.mz/imoveis-casas/terrenos/terrenos-venda/maputo-maputo/?search%5Bprivate_business%5D=private | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Rever/corrigir a nota de demonstração QA antes de publicar. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/32/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Rever/corrigir a nota de demonstração QA antes de publicar. historico_noticioso
4 173220 Alumínio “Verde” tempo Oportunidades 2026-06-16T12:41:02+00:00 2026-06-16T12:41:06+00:00 c61f05a56bb5a5f13d9d5d54dbefd270cf9ac40320917d77aaad76cac56c7b97 https://investemoz.tempo.co.mz/aluminio-verde 51 existente por integrar inventariado; cadastro/versão por reconciliar 50 setorial {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Ausente da biblioteca ativa e QA; falta exemplar versionado no manifesto de um novo lote e extensão nominal do importador. | Slug ausente no mapa do leitor; falta ligação ao percurso/dossier fundamentada para este artigo. | Não há setor mestre Alumínio Verde com correspondência exata; fundamentar o agrupamento sem inventar vínculo a projeto ou criar setor automaticamente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/50/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. setorial
5 73945 Banco Mundial classifica de proibitivos juros praticados em Moçambique tempo Nas Redes 2017-07-31T19:55:24+00:00 2017-07-31T19:55:24+00:00 36ba82b8885b00a4ea2fd9e4b1d8cf304a8e78e591972682f0aa53717f648d19 https://investemoz.tempo.co.mz/banco-mundial-classifica-de-proibitivos-juros-praticados-em-mocambique 44 existente por integrar inventariado; cadastro/versão por reconciliar 33 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/economia/38-economia/45893-banco-mundial-classifica-de-proibitivos-juros-praticados-em-mocambique.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Banco-Mundial-classifica-de-proibitivos-juros-praticados-em-Mo%C3%A7ambique.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1460805843956666 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/33/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
6 173213 Bens de Consumo e Franquias tempo Oportunidades 2026-06-16T11:31:06+00:00 2026-06-16T11:32:10+00:00 abd5f36a0c2b89d2989334137aae5d59b3158da38d556013ccd55deaa5069e92 https://investemoz.tempo.co.mz/bens-de-consumo-franquias 29 Publicado no ativo — setores-fintech-QA-d5b4dc354265 DOC01; importação privada, scan, metadados e publicação separados publicado inventariado; cadastro/versão por reconciliar 29 publicado publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Entrega concluída; nenhum novo envio ou republicação da imagem. Regulação específica Fintech permanece na matriz. Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Preservar publicação; restante acervo separado, sem repetir aceitação. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-fintech-investimento-QA-20261015/RESULTADO-QA.md; PROVA-DELTA-QA.json; HTTP-DELTA-QA.json; PROVA-FINAL-DELTA-QA.json; HTTP-RECUPERACAO-FINAL-QA.json; MANIFESTO-ENTREGA.json | outputs/investmoz-setores-fintech-ativacao-20261015/RELATORIO-ENTREGA.md; PUBLISHED.json; PRESERVACAO-FINAL.json; HTTP-OPERACIONAL.json; FRESH-BACKUP.json; RECIBO-FINAL.json outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/bens-de-consumo-franquias Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
7 172223 Cabo Delgado - Pemba, A capital do turismo costeiro do norte de Moçambique tempo Turismo 2026-04-14T10:21:20+00:00 2026-04-14T10:41:05+00:00 c96b2430c44b8edc9cc1d0a7974c23290dd0207cff366fea8326bb28bcab8285 https://investemoz.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique/ | https://investemoz.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique 20 publicado RecursoQA20; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 20 25 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
8 173482 Checklist para Contratação e Direitos do Trabalho Doméstico tempo Trabalho Doméstico 2026-09-15T12:36:11+00:00 2026-09-15T12:36:14+00:00 309b51ebc53bf840f9014da733bc09f8d162138be4e6e9d5a72670e26cc8e482 https://investemoz.tempo.co.mz/checklist-para-contratacao-e-direitos-do-trabalho-domestico 52 existente por integrar inventariado; cadastro/versão por reconciliar 52 trabalho_domestico {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/52/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. trabalho_domestico
9 76474 Coloque o seu número para o grupo de whatsapp, venda de acessórios de computador tempo Categoria 2017-08-08T17:29:51+00:00 2017-08-08T17:29:51+00:00 c8beeb94f936b3bfc69c720770bd84376e9f19b668d96185aaefbada05727010 https://investemoz.tempo.co.mz/coloque-o-seu-numero-para-o-grupo-de-whatsapp-venda-de-acessorios-de-computador 45 existente por integrar inventariado; cadastro/versão por reconciliar 34 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: https://www.facebook.com/photo.php?fbid=741844132690980&set=gm.1566816196708735&type=3 | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Coloque-o-seu-n%C3%BAmero-para-o-grupo-de-whatsapp-venda-de-acess%C3%B3rios-de-computador.jpg | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/1502293415_298_Coloque-o-seu-n%C3%BAmero-para-o-grupo-de-whatsapp-venda-de-acess%C3%B3rios-de-computador.jpg | Referência não recolhida: https://www.facebook.com/179463555444013/posts/1566816196708735 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/34/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
10 173484 Direitos Especiais da Trabalhadora Doméstica no Regulamento tempo Trabalho Doméstico 2026-09-15T12:38:08+00:00 2026-09-15T12:38:11+00:00 34c31bb02a3ad0886f12ac2853283de2ffb89a925be3cda73b7ce9385c6caec4 https://investemoz.tempo.co.mz/direitos-especiais-da-trabalhadora-domestica-no-regulamento 64 existente por integrar inventariado; cadastro/versão por reconciliar 53 trabalho_domestico {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/53/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. trabalho_domestico
11 171981 Energia & Mineração tempo Oportunidades 2026-04-06T09:17:46+00:00 2026-04-30T13:35:32+00:00 e9a95f0c8176eea48a75bd2c5bce69f15308806988fa5203a688af7c40305e9c https://investemoz.tempo.co.mz/energia-mineracao 7 publicado RecursoQA7; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 7 18 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 23, "sha256": "0a2c50d91dad3a6007544634540e0f61b11d3506682a5a19066bae03cdcba490"} {"scope": "homologação", "href": "/documentos/14/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/energia-mineracao Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
12 173222 Energia e Recursos Naturais tempo Oportunidades 2026-06-16T12:47:16+00:00 2026-06-16T12:51:30+00:00 6f8236b0441d61fce5171994ba6b82ffc8b5f2de3e8242de7be29248ed046ad2 https://investemoz.tempo.co.mz/energia-recursos-naturais 63 existente por integrar inventariado; cadastro/versão por reconciliar 51 setorial {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Ausente da biblioteca ativa e QA; falta exemplar versionado no manifesto de um novo lote e extensão nominal do importador. | Slug ausente no mapa do leitor; falta ligação ao percurso/dossier fundamentada para este artigo. | Texto distinto de Energia/Mineração já publicado; preservar a ligação declarada /energia-mineracao e delimitar agrupamento, sem fundir os exemplares. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/51/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. setorial
13 62414 Frelimo e Zanu-PF: Evocados laços de irmandade tempo Categoria 2017-06-26T12:03:20+00:00 2017-06-26T12:03:20+00:00 013307965b8fcb349a0d936d844ed12437bda4c90389f8efe958d4ffdb9ce62b https://investemoz.tempo.co.mz/frelimo-e-zanu-pf-evocados-lacos-de-irmandade 53 existente por integrar inventariado; cadastro/versão por reconciliar 35 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência indisponível na captura (HTTP 404): https://investemoz.tempo.co.mz/images/ANO-2017/JUNHO/21/GOVERNADOR-in.gif | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/35/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
14 62418 Governo dará todo O apoio à PGR no seguimento da auditoria tempo Categoria 2017-06-26T12:30:37+00:00 2017-06-26T12:30:45+00:00 65299b6711499e417be778b51c7ab7880616b0ccbf362854370eb775fc54caf3 https://investemoz.tempo.co.mz/governo-dara-todo-o-apoio-a-pgr-no-seguimento-da-auditoria 54 existente por integrar inventariado; cadastro/versão por reconciliar 36 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/a84c6c1e4d1780f63086a86817521b40 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/36/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
15 75002 Governo pretende construir 100 mil habitações em dois anos tempo Nas Redes 2017-08-04T20:01:55+00:00 2017-08-04T20:01:55+00:00 b32ea95039c2af387687738ed601f0947384eb7e19ca4bd282d603c60ca26afa https://investemoz.tempo.co.mz/governo-pretende-construir-100-mil-habitacoes-em-dois-anos 46 existente por integrar inventariado; cadastro/versão por reconciliar 37 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/45963-governo-pretende-construir-100-mil-habitacoes-em-dois-anos.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Governo-pretende-construir-100-mil-habita%C3%A7%C3%B5es-em-dois-anos.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1464602060243711 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/37/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
16 173478 Guia Prático para Empregadores sobre Contratação e Direitos no Trabalho Doméstico tempo Trabalho Doméstico 2026-09-15T12:30:34+00:00 2026-09-15T12:30:37+00:00 8038d8c221ba3cbbc4712304a6b17a6bba8ad946c8bac1a7836d472944aaf930 https://investemoz.tempo.co.mz/guia-pratico-para-empregadores-sobre-contratacao-e-direitos-no-trabalho-domestico 65 existente por integrar inventariado; cadastro/versão por reconciliar 54 trabalho_domestico {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/54/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. trabalho_domestico
17 173218 Imobiliário tempo Oportunidades 2026-06-16T11:34:45+00:00 2026-06-16T11:34:49+00:00 be8b606f087a939bd88a86f4436a0ff18c5fb774a179e6e915d4abb10a95f930 https://investemoz.tempo.co.mz/imobiliario 28 Publicado no ativo — setores-fintech-QA-d5b4dc354265 DOC01; importação privada, scan, metadados e publicação separados publicado inventariado; cadastro/versão por reconciliar 28 7 publicado publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Entrega concluída; nenhum novo envio ou republicação da imagem. Regulação específica Fintech permanece na matriz. Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Preservar publicação; restante acervo separado, sem repetir aceitação. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-fintech-investimento-QA-20261015/RESULTADO-QA.md; PROVA-DELTA-QA.json; HTTP-DELTA-QA.json; PROVA-FINAL-DELTA-QA.json; HTTP-RECUPERACAO-FINAL-QA.json; MANIFESTO-ENTREGA.json | outputs/investmoz-setores-fintech-ativacao-20261015/RELATORIO-ENTREGA.md; PUBLISHED.json; PRESERVACAO-FINAL.json; HTTP-OPERACIONAL.json; FRESH-BACKUP.json; RECIBO-FINAL.json outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/imobiliario Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
18 171992 Infraestruturas tempo Oportunidades 2026-04-06T09:25:07+00:00 2026-04-30T12:44:37+00:00 9716da16aba58eec173343f0bbd582d62d23a5eee7d20522c080c2da5da8ba66 https://investemoz.tempo.co.mz/infraestruturas 22 publicado RecursoQA22; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 22 17 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 26, "sha256": "8e58544e49e719f9c865c91e07ef1030155a9e3d59260e6dc93e9c9e25674e39"} {"scope": "homologação", "href": "/documentos/16/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/infraestruturas Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
19 172240 Maputo Cidade – Plataforma Estratégica de Turismo, Negócios e Investimento Sustentável tempo Turismo 2026-04-14T10:58:53+00:00 2026-04-14T11:03:28+00:00 0866639d09c5c0b3b00e8527554065587c793f60df978b66b6a578b3c741bc0d https://investemoz.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel/ | https://investemoz.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel 16 publicado RecursoQA16; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 16 21 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
20 171994 Mercado Financeiro tempo Oportunidades 2026-04-06T09:27:00+00:00 2026-04-17T09:53:42+00:00 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 https://investemoz.tempo.co.mz/mercado-financeiro 23 vazio — dependência concreta RecursoQA23; original/hash/ClamD/replay demonstrados. Corpo vazio na origemHTML/Inertia200;privado,sem página publicada. mercado_financeiro {"status": "capturada mas não publicada no ativo", "sha256": "417b4a33468f360acef272ce3e4180c1d0a171980befa73ddb9a33de5a9d4c4e"} Corpo do artigo vazio; imagem não substitui conteúdo. Manter pendente e separado, sem bloquear outros lotes. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado Original vazio Fornecer corpo editorial pela Tempo; imagem e Fintech não substituem o original. Sem novo endereço público.
21 173480 Modelo de Contrato de Trabalho Doméstico para Pequenas Famílias tempo Trabalho Doméstico 2026-09-15T12:34:17+00:00 2026-09-15T12:34:19+00:00 5b95ad9010fcbdc1000052f47766ca54409d3e0649211729ced9ef142f5c6db0 https://investemoz.tempo.co.mz/modelo-de-contrato-de-trabalho-domestico-para-pequenas-familias 66 existente por integrar inventariado; cadastro/versão por reconciliar 55 trabalho_domestico {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/55/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. trabalho_domestico
22 172219 Niassa - O último grande destino virgem do turismo africano tempo Turismo 2026-04-14T10:17:14+00:00 2026-04-14T10:43:56+00:00 47d82a59a86c3cbdef04ea065e711e7139d50137e440f03efbd710e3c4220ef7 https://investemoz.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano/ | https://investemoz.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano 21 publicado RecursoQA21; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 21 26 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
23 173476 Procedimentos para Rescisão do Contrato de Trabalho Doméstico segundo o Regulamento de Moçambique tempo Trabalho Doméstico 2026-09-15T11:39:21+00:00 2026-09-15T11:39:24+00:00 bb634a9021da9d7b46ed105317aa54e4af42a16dab042e0ceb2bdc0d702d1a67 https://investemoz.tempo.co.mz/procedimentos-para-rescisao-do-contrato-de-trabalho-domestico-segundo-o-regulamento-de-mocambique 67 existente por integrar inventariado; cadastro/versão por reconciliar 56 trabalho_domestico {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/56/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. trabalho_domestico
24 172263 Província da Zambézia: Onde os Corredores de Moçambique Convergem com a Natureza, a Cultura e o Turismo Sustentável tempo Turismo 2026-04-14T14:21:52+00:00 2026-04-14T14:26:27+00:00 9e7ca4e9542135d5969f5c008ce8e29b037cc06a53875cfa7dc3e7452ec47744 https://investemoz.tempo.co.mz/provincia-da-zambezia-onde-os-corredores-de-mocambique-convergem-com-a-natureza-a-cultura-e-o-turismo-sustentavel 11 publicado RecursoQA11; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 11 3 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/provincia-da-zambezia-onde-os-corredores-de-mocambique-convergem-com-a-natureza-a-cultura-e-o-turismo-sustentavel Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
25 172233 Província de Gaza: Oportunidades mais sólidas e estratégicas de investimento no turismo no sul de Moçambique. tempo Turismo 2026-04-14T10:52:30+00:00 2026-04-17T08:28:56+00:00 2bf2df9bd3b7ab8656209a48f4928b5d8bbf2959e77c292fc4c9987e67ba2fea https://investemoz.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique/ | https://investemoz.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique 18 publicado RecursoQA18; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 18 23 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
26 172253 Província de Inhambane: Um Destino Estratégico para Investimento Turístico tempo Turismo 2026-04-14T14:15:12+00:00 2026-04-14T14:25:44+00:00 2d1cc729481ce65a5e50898c0eea4a1eb2418273b03fc749009e2ffaaa72fe71 https://investemoz.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico/ | https://investemoz.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico 15 publicado RecursoQA15; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 15 6 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
27 172258 Província de Manica: Onde o Turismo de Montanha, Natureza e Cultura Encontra o Próximo Ciclo de Investimento tempo Turismo 2026-04-14T14:18:28+00:00 2026-04-17T10:42:16+00:00 4c8bdd79212ba98fa347733e2f58aef8796b29cc8cb8b649d200bd37435df1c3 https://investemoz.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento/ | https://investemoz.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento 14 publicado RecursoQA14; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 14 20 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
28 172237 Província de Maputo: Natureza, Cultura e Conectividade como Motores de Crescimento Sustentável tempo Turismo 2026-04-14T10:55:21+00:00 2026-04-17T08:27:24+00:00 d112d0e535912f4487c2b8d41978e5e663f1db43f1a2cdf170dbbec535f13f27 https://investemoz.tempo.co.mz/provincia-de-maputo-turismo/ | https://investemoz.tempo.co.mz/provincia-de-maputo-turismo 17 publicado RecursoQA17; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 17 22 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/provincia-de-maputo-turismo Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
29 172230 Província de Nampula - Perfil de investimento no setor do turismo tempo Turismo 2026-04-14T10:50:32+00:00 2026-04-14T11:10:23+00:00 d18ccc1e036b3480605462ce7176be75153f6aa988bc9b37d8d395b01e555a7a https://investemoz.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo/ | https://investemoz.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo 19 publicado RecursoQA19; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 19 24 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
30 172256 Província de Sofala: Onde a Natureza, a História e o Futuro do Turismo Convergem tempo Turismo 2026-04-14T14:16:47+00:00 2026-04-14T14:26:17+00:00 12b46a34ba61c6ef0058a6c410ebc17329d897fe4b06a13b4ea3fe3c85a28342 https://investemoz.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem/ | https://investemoz.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem 12 publicado RecursoQA12; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 12 4 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
31 172261 Província de Tete: Onde o Rio Zambeze, a Cultura Viva e a Conservação Comunitária Criam o Futuro do Turismo tempo Turismo 2026-04-14T14:19:48+00:00 2026-04-14T14:26:25+00:00 e78fce2931260a7d6f82270897e906feb640f65d084c2d4ffb5ba19303858275 https://investemoz.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo/ | https://investemoz.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo 13 publicado RecursoQA13; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 13 5 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
32 73246 Rofino Licuco preso cinco meses depois de condenado por agressão tempo Nas Redes 2017-07-28T20:23:11+00:00 2017-07-28T20:23:11+00:00 806c54948641f4f5933a2ea4a6935dfb7a67202ab227960451d62435880289f6 https://investemoz.tempo.co.mz/rofino-licuco-preso-cinco-meses-depois-de-condenado-por-agressao 47 existente por integrar inventariado; cadastro/versão por reconciliar 38 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/45870-rofino-licuco-preso-cinco-meses-depois-de-condenado-por-agressao-.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Rofino-Licuco-preso-cinco-meses-depois-de-condenado-por-agress%C3%A3o.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1458270617543522 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/38/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
33 77232 Sete indivíduos mortos a tiro na Matola tempo Nas Redes 2017-08-11T20:30:32+00:00 2017-08-11T20:30:32+00:00 e657a97d5d6db7fa0ea6b7d14029ff873ea5d36dfdadda5f8b9bc8f1ef5dd011 https://investemoz.tempo.co.mz/sete-individuos-mortos-a-tiro-na-matola 48 existente por integrar inventariado; cadastro/versão por reconciliar 39 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/46074-sete-individuos-mortos-a-tiro-na-matola-.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Sete-indiv%C3%ADduos-mortos-a-tiro-na-Matola.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1471269102910340 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/39/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
34 62416 Sete pessoas morrem e 150 são resgatados em acidente com eléctrico na Índia tempo Categoria 2017-06-26T12:14:48+00:00 2017-06-26T12:14:48+00:00 0fe20a9ca9dd99c58072f43b3dece1de347ef4063ee5f75bcffe8b2f4bf253b5 https://investemoz.tempo.co.mz/sete-pessoas-morrem-e-150-sao-resgatados-em-acidente-com-electrico-na-india 55 existente por integrar inventariado; cadastro/versão por reconciliar 40 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://www.verdade.co.mz/internacional/62604-sete-pessoas-morrem-e-150-sao-resgatados-em-acidente-com-electrico-na-india | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/40/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
35 173209 Fintech tempo Oportunidades 2026-06-16T11:24:22+00:00 2026-06-23T10:03:06+00:00 8db9206bf5192b68e7c9bdfb87e9dc9c7063a4b7eca56630685b0e6c294f55ab https://investemoz.tempo.co.mz/tech-fintech 27 Publicado no ativo — setores-fintech-QA-d5b4dc354265 DOC01; importação privada, scan, metadados e publicação separados publicado inventariado; cadastro/versão por reconciliar 27 28 publicado publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Entrega concluída; nenhum novo envio ou republicação da imagem. Regulação específica Fintech permanece na matriz. Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Preservar publicação; restante acervo separado, sem repetir aceitação. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-fintech-investimento-QA-20261015/RESULTADO-QA.md; PROVA-DELTA-QA.json; HTTP-DELTA-QA.json; PROVA-FINAL-DELTA-QA.json; HTTP-RECUPERACAO-FINAL-QA.json; MANIFESTO-ENTREGA.json | outputs/investmoz-setores-fintech-ativacao-20261015/RELATORIO-ENTREGA.md; PUBLISHED.json; PRESERVACAO-FINAL.json; HTTP-OPERACIONAL.json; FRESH-BACKUP.json; RECIBO-FINAL.json outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/tech-fintech Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
36 171990 Turismo tempo Oportunidades 2026-04-06T09:22:51+00:00 2026-06-16T13:07:59+00:00 604d30a740cc6ed20d24e3447806e733aa4aefb9f339f81d3dc57b644dd19fb7 https://investemoz.tempo.co.mz/turismo 10 publicado RecursoQA10; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 10 19 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 25, "sha256": "fe9a2e382e385b35f6e61ae320e199a979678677fd327fb7d4b07d8ce0111c11"} {"scope": "homologação", "href": "/documentos/15/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/turismo Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
37 74364 tempo Categoria 2017-07-26T21:20:33+00:00 2017-07-26T21:20:33+00:00 2a7f0a5b09e0136fec9eae357ac8a88a159ead23b25ed7bb735c75997b10ec5e https://investemoz.tempo.co.mz/74364-2 dependência concreta — título ausente inventariado; cadastro/versão por reconciliar identidade_incompleta {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/1501695692_133_notitle.jpg | Referência não recolhida: https://www.facebook.com/179463555444013/posts/1552815684775453 | Título vazio no post 74364; corpo com 20 caracteres; identidade editorial a esclarecer sem inventar título. Preservar exemplar e referências; delimitar recuperação editorial antes de integrar. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado Original sem título Identificação editorial pela Tempo para o post 74364; corpo com 21 caracteres úteis preservado. Sem título ou endereço novo inventado.
38 62929 Daúde Razaque já não é treinador da Liga Desportiva de Maputo tempo Nas Redes 2017-06-27T16:41:43+00:00 2017-06-27T16:41:43+00:00 c3b57ea685290bfb01c50403bc83db98cf1a42ad51c04d5b8d288c99b8c0a808 https://investemoz.tempo.co.mz/daude-razaque-ja-nao-e-treinador-da-liga-desportiva-de-maputo 49 existente por integrar inventariado; cadastro/versão por reconciliar 41 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/desporto/74-desporto/45358-daude-razaque-ja-nao-e-treinador-da-liga-desportiva-de-maputo.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Da%C3%BAde-Razaque-j%C3%A1-n%C3%A3o-%C3%A9-treinador-da-Liga-Desportiva-de-Maputo.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1425759480794636 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/41/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
39 62649 Daviz Simango não se incomoda com possível sucessão na Beira tempo Nas Redes 2017-06-26T19:38:09+00:00 2017-06-26T19:38:09+00:00 090e5f41ed11c8e680d2e30ea5c8310b7c6b165ddec318a8b0cf94927f78da74 https://investemoz.tempo.co.mz/daviz-simango-nao-se-incomoda-com-possivel-sucessao-na-beira 56 existente por integrar inventariado; cadastro/versão por reconciliar 42 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/politica/63-politica/45340-daviz-simango-nao-se-incomoda-com-possivel-sucessao-na-beira-.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Daviz-Simango-n%C3%A3o-se-incomoda-com-poss%C3%ADvel-sucess%C3%A3o-na-Beira.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1424683617568889 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/42/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
40 173470 Direitos e Deveres do Trabalhador Doméstico tempo Trabalho Doméstico 2026-09-15T11:28:25+00:00 2026-09-15T11:28:28+00:00 f69fd6fd8509913f2d79df0267c469dc7a9d0d30420ce8428749d53ba21446ab https://investemoz.tempo.co.mz/direitos-e-deveres-do-trabalhador-domestico 68 existente por integrar inventariado; cadastro/versão por reconciliar 57 trabalho_domestico {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/57/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. trabalho_domestico
41 172679 Energia tempo Featured 2026-04-30T13:03:57+00:00 2026-04-30T13:28:48+00:00 e2a7eb4be0a2cf4139737f6428c147ac35de99899478ce79195dc5e1b0a960f3 https://investemoz.tempo.co.mz/energia | https://investemoz.tempo.co.mz/energia/ 8 publicado RecursoQA8; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 8 1 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/energia Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
42 62739 “Exigimos responsabilização dos agentes do crime”, Daviz Simango tempo Nas Redes 2017-06-26T23:13:36+00:00 2017-06-26T23:13:36+00:00 e37ea18176246dab529d2ecec7734f0fb2b0a54cadc507fbd789033d5b8e5b93 https://investemoz.tempo.co.mz/exigimos-responsabilizacao-dos-agentes-do-crime-daviz-simango-2 57 existente por integrar inventariado; cadastro/versão por reconciliar 43 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/politica/63-politica/45345-exigimos-responsabilizacao-dos-agentes-do-crime-daviz-simango.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/%E2%80%9CExigimos-responsabiliza%C3%A7%C3%A3o-dos-agentes-do-crime%E2%80%9D-Daviz-Simango.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1424873994216518 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/43/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
43 62854 Governo e parceiros reforçam mecanismos de controlo de HIV/Sida tempo Nas Redes 2017-06-27T11:07:53+00:00 2017-06-27T11:07:53+00:00 0073816b1fa4bd50ec6417b07272da06565230d06a19079ca76cb006c882d2f1 https://investemoz.tempo.co.mz/governo-e-parceiros-reforcam-mecanismos-de-controlo-de-hivsida 58 existente por integrar inventariado; cadastro/versão por reconciliar 44 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/45352-governo-e-parceiros-reforcam-mecanismos-de-controlo-de-hivsida.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Governo-e-parceiros-refor%C3%A7am-mecanismos-de-controlo-de-HIVSida.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1425451974158720 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/44/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
44 63128 Graça Machel diz que desnutrição é um problema de desenvolvimento tempo Nas Redes 2017-06-27T22:01:59+00:00 2017-06-27T22:01:59+00:00 711f3707e3a0d72ec919216fc50823de9060349104ee98fe0a6b20d6684b6031 https://investemoz.tempo.co.mz/graca-machel-diz-que-desnutricao-e-um-problema-de-desenvolvimento 59 existente por integrar inventariado; cadastro/versão por reconciliar 45 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/sociedade/45-sociedade/45368-graca-machel-diz-que-desnutricao-e-um-problema-de-desenvolvimento.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Gra%C3%A7a-Machel-diz-que-desnutri%C3%A7%C3%A3o-%C3%A9-um-problema-de-desenvolvimento.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1426040144099903 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/45/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
45 63181 In dependência tempo Nas Redes 2017-06-27T21:38:06+00:00 2017-06-27T21:38:06+00:00 b0d87c0d58d6d1343d102cd3141bda1215c5afd54f3780a1c6c262d786dbfe27 https://investemoz.tempo.co.mz/in-dependencia 60 existente por integrar inventariado; cadastro/versão por reconciliar 46 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://opais.sapo.mz/index.php/opiniao/182-helder-faife/45364-in-dependencia.html | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/In-depend%C3%AAncia.jpg | Referência não recolhida: https://www.facebook.com/196034093767187/posts/1426022220768362 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/46/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
46 63146 Kroll não descobriu como foram gastos 2 biliões de dólares; Governo de Nyusi não apoiou a Auditoria; criação da Proindicus, EMATUM e MAM não foi para proteção da Zona Económica Exclusiva tempo Nas Redes 2017-06-27T19:30:01+00:00 2017-06-27T19:30:01+00:00 4ccbad5c92ddc3884f58181a4f1ed3c1d3d6a59f005bfd99b197a6fc1fc3c280 https://investemoz.tempo.co.mz/kroll-nao-descobriu-como-foram-gastos-2-bilioes-de-dolares-governo-de-nyusi-nao-apoiou-a-auditoria-criacao-da-proindicus-ematum-e-mam-nao-foi-para-protecao-da-zona-economica-exclusiva-2 61 existente por integrar inventariado; cadastro/versão por reconciliar 47 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://www.verdade.co.mz/tema-de-fundo/35/62615 | Referência não recolhida: http://www.newsaiep.com/moz_news/wp-content/uploads/Kroll-n%C3%A3o-descobriu-como-foram-gastos-2-bili%C3%B5es-de-d%C3%B3lares-Governo-de-Nyusi-n%C3%A3o-apoiou-a-Auditoria-cria%C3%A7%C3%A3o-da-Proindicus-EMATUM-e-MAM-n%C3%A3o-foi-para-prote%C3%A7%C3%A3o-da-Zona-Econ%C3%B3mica-Exclusiva.jpg | Referência não recolhida: https://www.facebook.com/113487338672967/posts/1492451004109920 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/47/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
47 172674 Mineração tempo Featured 2026-04-30T13:12:09+00:00 2026-04-30T13:30:13+00:00 8d3acc292ed806bc4cc7bcb6a3a6a5759f30b610766a5ef4b3d6a0b7c56d5861 https://investemoz.tempo.co.mz/mineracao 9 publicado RecursoQA9; original/hash/ClamD/replay demonstrados. DivulgaçãoQAseparada;consumidorHTTP/desktop observado;não é oportunidade nem geometria. 9 2 publicados {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} {"scope": "homologação", "href": "/documentos/27/view", "status": "referência na ficha; binário não reensaiado"} Sem pendência de integração desta identidade; mantém-se a revisão editorial corrente. Reutilizar a versão e a aceitação existentes; não importar ou aceitar novamente. Preservar a publicação; gerir edições e versões pela biblioteca existente. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) outputs/legacy-correspondence-20261022/public-pages.json; qa-import-receipt.json; editorial-gaps-current.json; inventário histórico de 15/10 preservado https://teste-investidor.tempo.co.mz/mineracao Publicado; identidade e conteúdo consultáveis na homologação Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. 2026-10-22T21:05:03.369388+00:00
48 62688 O Instituto Nacional de Meteorologia prevê o seguinte estado do tempo para esta ... tempo Nas Redes 2017-06-26T18:21:18+00:00 2017-06-26T18:21:18+00:00 396fe2575641622015505eb11d4c7152b6e3e1b741966f14c0cd1673fd455fd3 https://investemoz.tempo.co.mz/o-instituto-nacional-de-meteorologia-preve-o-seguinte-estado-do-tempo-para-esta-29 62 existente por integrar inventariado; cadastro/versão por reconciliar 48 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://www.verdade.co.mz/newsflash/62607 | Referência não recolhida: https://www.facebook.com/113487338672967/posts/1491493337539020 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/48/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
49 173474 Sanções Disciplinares Aplicáveis a Trabalhadores Domésticos segundo o Regulamento de Moçambique tempo Trabalho Doméstico 2026-09-15T11:37:04+00:00 2026-09-15T11:37:06+00:00 e09a0fe71397e2b060ff8435111117aa487b476a85ec522fffcdf9e23a6cdac2 https://investemoz.tempo.co.mz/sancoes-disciplinares-aplicaveis-a-trabalhadores-domesticos-segundo-o-regulamento-de-mocambique 69 existente por integrar inventariado; cadastro/versão por reconciliar 58 trabalho_domestico {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/58/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. trabalho_domestico
50 63248 Dois alunos da 9ª classe caíram nas mãos da Polícia da República de Moçambique (... tempo Nas Redes 2017-06-28T11:30:01+00:00 2017-06-28T11:30:01+00:00 2220ca2c07d10f10b2bd6fd0f103ee733b83570d436c751c449c85b42df46a13 https://investemoz.tempo.co.mz/dois-alunos-da-9a-classe-cairam-nas-maos-da-policia-da-republica-de-mocambique 50 existente por integrar inventariado; cadastro/versão por reconciliar 49 historico_noticioso {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Referência não recolhida: http://www.verdade.co.mz/newsflash/62625 | Referência não recolhida: https://www.facebook.com/113487338672967/posts/1493133914041629 | Notícia/anúncio histórico; definir destino de consulta e prioridade pela Tempo, sem converter em orientação atual ou oportunidade vigente. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/49/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. historico_noticioso
51 173463 Regulamento do Trabalho Doméstico tempo Trabalho Doméstico 2026-09-15T10:56:07+00:00 2026-09-15T12:41:44+00:00 eb43b6c1f097631250f0b36d3d01b4d106db2fcae07a56bb154819143fff11e9 https://investemoz.tempo.co.mz/regulamento-do-trabalho-domestico 70 existente por integrar inventariado; cadastro/versão por reconciliar 59 trabalho_domestico {"status": "imagem já publicada; reutilizar", "resource_id": 24, "sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"} Conteúdo editorial não oficial; falta recorte e ligação ao consumidor/apoio apropriado. Eventual uso jurídico ou cálculo requer citação/âmbito, sem revalidar credibilidade de fontes oficiais já aceites. Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Preparar apenas se escolhido para o próximo lote; não publicar por esta reconciliação. Rever pela gestão; corrigir e pré-visualizar; publicar expressamente quando autorizado, sem comandos. outputs/investmoz-legado-inventario-reconciliado-20261015/RECONCILIACAO-LEGADO.json; LEITURA-ESTADO.json (SQL READ ONLY) docs/investmoz/unification/legacy-editorial-installation-receipt-20261022.json; preservação dos registos anteriores conferida; provas QA reutilizadas https://teste-investidor.tempo.co.mz/management/portal-services/resources/59/article/preview Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. trabalho_domestico
@@ -0,0 +1,58 @@
# Correspondência integral dos 50 artigos do legado
Conferência HTTP individual em 2026-10-22T21:05:03.369388+00:00. Lista por identidade editorial; imagens, recursos da biblioteca e variantes de URL não são artigos adicionais. Não altera publicações. Endereço em branco significa que não foi demonstrada uma página nova, não uma ligação a criar por suposição.
Actualização de homologação, 22/10/2026: os 28 estão cadastrados privadamente, com pesquisa/editor/prévia/recuperação conferidos. Os seus endereços abaixo são privados e exigem conta autorizada; não são artigos publicados. Os 20 públicos conservam a conferência anterior, sem novas consultas. Recibo: `../../unification/legacy-editorial-installation-receipt-20261022.json`.
| ID | Título original | Endereço original | Endereço no novo portal | Estado | Pendência |
| --- | --- | --- | --- | --- | --- |
| 171985 | Agronegocio | https://investemoz.tempo.co.mz/agronegocio | https://teste-investidor.tempo.co.mz/agronegocio | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 62413 | 15/30 costa do sol com dua na zona de mupulene muito lindo | https://investemoz.tempo.co.mz/1530-costa-do-sol-com-dua-na-zona-de-mupulene-muito-lindo | https://teste-investidor.tempo.co.mz/management/portal-services/resources/32/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. Rever/corrigir a nota de demonstração QA antes de publicar. |
| 173220 | Alumínio “Verde” | https://investemoz.tempo.co.mz/aluminio-verde | https://teste-investidor.tempo.co.mz/management/portal-services/resources/50/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 73945 | Banco Mundial classifica de proibitivos juros praticados em Moçambique | https://investemoz.tempo.co.mz/banco-mundial-classifica-de-proibitivos-juros-praticados-em-mocambique | https://teste-investidor.tempo.co.mz/management/portal-services/resources/33/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 173213 | Bens de Consumo e Franquias | https://investemoz.tempo.co.mz/bens-de-consumo-franquias | https://teste-investidor.tempo.co.mz/bens-de-consumo-franquias | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172223 | Cabo Delgado - Pemba, A capital do turismo costeiro do norte de Moçambique | https://investemoz.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique/ \| https://investemoz.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique | https://teste-investidor.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 173482 | Checklist para Contratação e Direitos do Trabalho Doméstico | https://investemoz.tempo.co.mz/checklist-para-contratacao-e-direitos-do-trabalho-domestico | https://teste-investidor.tempo.co.mz/management/portal-services/resources/52/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 76474 | Coloque o seu número para o grupo de whatsapp, venda de acessórios de computador | https://investemoz.tempo.co.mz/coloque-o-seu-numero-para-o-grupo-de-whatsapp-venda-de-acessorios-de-computador | https://teste-investidor.tempo.co.mz/management/portal-services/resources/34/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 173484 | Direitos Especiais da Trabalhadora Doméstica no Regulamento | https://investemoz.tempo.co.mz/direitos-especiais-da-trabalhadora-domestica-no-regulamento | https://teste-investidor.tempo.co.mz/management/portal-services/resources/53/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 171981 | Energia & Mineração | https://investemoz.tempo.co.mz/energia-mineracao | https://teste-investidor.tempo.co.mz/energia-mineracao | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 173222 | Energia e Recursos Naturais | https://investemoz.tempo.co.mz/energia-recursos-naturais | https://teste-investidor.tempo.co.mz/management/portal-services/resources/51/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 62414 | Frelimo e Zanu-PF: Evocados laços de irmandade | https://investemoz.tempo.co.mz/frelimo-e-zanu-pf-evocados-lacos-de-irmandade | https://teste-investidor.tempo.co.mz/management/portal-services/resources/35/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 62418 | Governo dará todo O apoio à PGR no seguimento da auditoria | https://investemoz.tempo.co.mz/governo-dara-todo-o-apoio-a-pgr-no-seguimento-da-auditoria | https://teste-investidor.tempo.co.mz/management/portal-services/resources/36/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 75002 | Governo pretende construir 100 mil habitações em dois anos | https://investemoz.tempo.co.mz/governo-pretende-construir-100-mil-habitacoes-em-dois-anos | https://teste-investidor.tempo.co.mz/management/portal-services/resources/37/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 173478 | Guia Prático para Empregadores sobre Contratação e Direitos no Trabalho Doméstico | https://investemoz.tempo.co.mz/guia-pratico-para-empregadores-sobre-contratacao-e-direitos-no-trabalho-domestico | https://teste-investidor.tempo.co.mz/management/portal-services/resources/54/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 173218 | Imobiliário | https://investemoz.tempo.co.mz/imobiliario | https://teste-investidor.tempo.co.mz/imobiliario | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 171992 | Infraestruturas | https://investemoz.tempo.co.mz/infraestruturas | https://teste-investidor.tempo.co.mz/infraestruturas | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172240 | Maputo Cidade – Plataforma Estratégica de Turismo, Negócios e Investimento Sustentável | https://investemoz.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel/ \| https://investemoz.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel | https://teste-investidor.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 171994 | Mercado Financeiro | https://investemoz.tempo.co.mz/mercado-financeiro | | Original vazio | Fornecer corpo editorial pela Tempo; imagem e Fintech não substituem o original. Sem novo endereço público. |
| 173480 | Modelo de Contrato de Trabalho Doméstico para Pequenas Famílias | https://investemoz.tempo.co.mz/modelo-de-contrato-de-trabalho-domestico-para-pequenas-familias | https://teste-investidor.tempo.co.mz/management/portal-services/resources/55/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 172219 | Niassa - O último grande destino virgem do turismo africano | https://investemoz.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano/ \| https://investemoz.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano | https://teste-investidor.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 173476 | Procedimentos para Rescisão do Contrato de Trabalho Doméstico segundo o Regulamento de Moçambique | https://investemoz.tempo.co.mz/procedimentos-para-rescisao-do-contrato-de-trabalho-domestico-segundo-o-regulamento-de-mocambique | https://teste-investidor.tempo.co.mz/management/portal-services/resources/56/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 172263 | Província da Zambézia: Onde os Corredores de Moçambique Convergem com a Natureza, a Cultura e o Turismo Sustentável | https://investemoz.tempo.co.mz/provincia-da-zambezia-onde-os-corredores-de-mocambique-convergem-com-a-natureza-a-cultura-e-o-turismo-sustentavel | https://teste-investidor.tempo.co.mz/provincia-da-zambezia-onde-os-corredores-de-mocambique-convergem-com-a-natureza-a-cultura-e-o-turismo-sustentavel | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172233 | Província de Gaza: Oportunidades mais sólidas e estratégicas de investimento no turismo no sul de Moçambique. | https://investemoz.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique/ \| https://investemoz.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique | https://teste-investidor.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172253 | Província de Inhambane: Um Destino Estratégico para Investimento Turístico | https://investemoz.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico/ \| https://investemoz.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico | https://teste-investidor.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172258 | Província de Manica: Onde o Turismo de Montanha, Natureza e Cultura Encontra o Próximo Ciclo de Investimento | https://investemoz.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento/ \| https://investemoz.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento | https://teste-investidor.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172237 | Província de Maputo: Natureza, Cultura e Conectividade como Motores de Crescimento Sustentável | https://investemoz.tempo.co.mz/provincia-de-maputo-turismo/ \| https://investemoz.tempo.co.mz/provincia-de-maputo-turismo | https://teste-investidor.tempo.co.mz/provincia-de-maputo-turismo | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172230 | Província de Nampula - Perfil de investimento no setor do turismo | https://investemoz.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo/ \| https://investemoz.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo | https://teste-investidor.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172256 | Província de Sofala: Onde a Natureza, a História e o Futuro do Turismo Convergem | https://investemoz.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem/ \| https://investemoz.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem | https://teste-investidor.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 172261 | Província de Tete: Onde o Rio Zambeze, a Cultura Viva e a Conservação Comunitária Criam o Futuro do Turismo | https://investemoz.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo/ \| https://investemoz.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo | https://teste-investidor.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 73246 | Rofino Licuco preso cinco meses depois de condenado por agressão | https://investemoz.tempo.co.mz/rofino-licuco-preso-cinco-meses-depois-de-condenado-por-agressao | https://teste-investidor.tempo.co.mz/management/portal-services/resources/38/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 77232 | Sete indivíduos mortos a tiro na Matola | https://investemoz.tempo.co.mz/sete-individuos-mortos-a-tiro-na-matola | https://teste-investidor.tempo.co.mz/management/portal-services/resources/39/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 62416 | Sete pessoas morrem e 150 são resgatados em acidente com eléctrico na Índia | https://investemoz.tempo.co.mz/sete-pessoas-morrem-e-150-sao-resgatados-em-acidente-com-electrico-na-india | https://teste-investidor.tempo.co.mz/management/portal-services/resources/40/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 173209 | Fintech | https://investemoz.tempo.co.mz/tech-fintech | https://teste-investidor.tempo.co.mz/tech-fintech | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 171990 | Turismo | https://investemoz.tempo.co.mz/turismo | https://teste-investidor.tempo.co.mz/turismo | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 74364 | | https://investemoz.tempo.co.mz/74364-2 | | Original sem título | Identificação editorial pela Tempo para o post 74364; corpo com 21 caracteres úteis preservado. Sem título ou endereço novo inventado. |
| 62929 | Daúde Razaque já não é treinador da Liga Desportiva de Maputo | https://investemoz.tempo.co.mz/daude-razaque-ja-nao-e-treinador-da-liga-desportiva-de-maputo | https://teste-investidor.tempo.co.mz/management/portal-services/resources/41/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 62649 | Daviz Simango não se incomoda com possível sucessão na Beira | https://investemoz.tempo.co.mz/daviz-simango-nao-se-incomoda-com-possivel-sucessao-na-beira | https://teste-investidor.tempo.co.mz/management/portal-services/resources/42/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 173470 | Direitos e Deveres do Trabalhador Doméstico | https://investemoz.tempo.co.mz/direitos-e-deveres-do-trabalhador-domestico | https://teste-investidor.tempo.co.mz/management/portal-services/resources/57/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 172679 | Energia | https://investemoz.tempo.co.mz/energia \| https://investemoz.tempo.co.mz/energia/ | https://teste-investidor.tempo.co.mz/energia | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 62739 | “Exigimos responsabilização dos agentes do crime”, Daviz Simango | https://investemoz.tempo.co.mz/exigimos-responsabilizacao-dos-agentes-do-crime-daviz-simango-2 | https://teste-investidor.tempo.co.mz/management/portal-services/resources/43/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 62854 | Governo e parceiros reforçam mecanismos de controlo de HIV/Sida | https://investemoz.tempo.co.mz/governo-e-parceiros-reforcam-mecanismos-de-controlo-de-hivsida | https://teste-investidor.tempo.co.mz/management/portal-services/resources/44/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 63128 | Graça Machel diz que desnutrição é um problema de desenvolvimento | https://investemoz.tempo.co.mz/graca-machel-diz-que-desnutricao-e-um-problema-de-desenvolvimento | https://teste-investidor.tempo.co.mz/management/portal-services/resources/45/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 63181 | In dependência | https://investemoz.tempo.co.mz/in-dependencia | https://teste-investidor.tempo.co.mz/management/portal-services/resources/46/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 63146 | Kroll não descobriu como foram gastos 2 biliões de dólares; Governo de Nyusi não apoiou a Auditoria; criação da Proindicus, EMATUM e MAM não foi para proteção da Zona Económica Exclusiva | https://investemoz.tempo.co.mz/kroll-nao-descobriu-como-foram-gastos-2-bilioes-de-dolares-governo-de-nyusi-nao-apoiou-a-auditoria-criacao-da-proindicus-ematum-e-mam-nao-foi-para-protecao-da-zona-economica-exclusiva-2 | https://teste-investidor.tempo.co.mz/management/portal-services/resources/47/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 172674 | Mineração | https://investemoz.tempo.co.mz/mineracao | https://teste-investidor.tempo.co.mz/mineracao | Publicado; identidade e conteúdo consultáveis na homologação | Não foi feita aceitação visual ou conferência pela Tempo nesta passagem. |
| 62688 | O Instituto Nacional de Meteorologia prevê o seguinte estado do tempo para esta ... | https://investemoz.tempo.co.mz/o-instituto-nacional-de-meteorologia-preve-o-seguinte-estado-do-tempo-para-esta-29 | https://teste-investidor.tempo.co.mz/management/portal-services/resources/48/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 173474 | Sanções Disciplinares Aplicáveis a Trabalhadores Domésticos segundo o Regulamento de Moçambique | https://investemoz.tempo.co.mz/sancoes-disciplinares-aplicaveis-a-trabalhadores-domesticos-segundo-o-regulamento-de-mocambique | https://teste-investidor.tempo.co.mz/management/portal-services/resources/58/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 63248 | Dois alunos da 9ª classe caíram nas mãos da Polícia da República de Moçambique (... | https://investemoz.tempo.co.mz/dois-alunos-da-9a-classe-cairam-nas-maos-da-policia-da-republica-de-mocambique | https://teste-investidor.tempo.co.mz/management/portal-services/resources/49/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
| 173463 | Regulamento do Trabalho Doméstico | https://investemoz.tempo.co.mz/regulamento-do-trabalho-domestico | https://teste-investidor.tempo.co.mz/management/portal-services/resources/59/article/preview | Rascunho privado na homologação; pesquisa, editor, prévia e recuperação conferidos | Revisão editorial e publicação separada pela equipa no portal. As referências sem exemplar de imagem verificado permanecem explícitas. |
@@ -0,0 +1,545 @@
{
"checked_utc": "2026-10-22T21:05:03.369388+00:00",
"distinct_articles": 50,
"publications_documented": 20,
"public_pages_identity_verified": 20,
"pending_content": 28,
"private_preparation": {
"historico_noticioso": 18,
"setorial": 2,
"trabalho_domestico": 8
},
"empty": 1,
"untitled": 1,
"database_writes_by_correspondence_script": 0,
"active_database_writes": 0,
"new_publications": 0,
"qa_imports": 28,
"qa_clean_scans": 28,
"remaining_integration_complete": false,
"public_pages": {
"checked_utc": "2026-10-22T21:05:03.369388+00:00",
"method": "Anonymous HTTP GET; Inertia article identity/source/content checked; no visual acceptance or new publication",
"pages": {
"173209": {
"url": "https://teste-investidor.tempo.co.mz/tech-fintech",
"status": 200,
"identity_verified": true,
"resource_id": 28,
"document_sha256": "926f5e4096737c936c9d5002ebdc64be9e63b0c0822023d17ce618111a65c941",
"current_title": "Fintech",
"image_url": "/documentos/27/view",
"response_sha256": "565f86bb5febd5833d5392588551b8e587b2d65898eda06284dcb33228866b63"
},
"173218": {
"url": "https://teste-investidor.tempo.co.mz/imobiliario",
"status": 200,
"identity_verified": true,
"resource_id": 7,
"document_sha256": "43612c358c7573475cea24ef4825c5b7c7925306fa5861051220eed3f4e333b5",
"current_title": "Imobiliário",
"image_url": "/documentos/27/view",
"response_sha256": "684840410238c5a199c581373d5cab233cdf869ccbf7d0da306a3d26181060b4"
},
"173213": {
"url": "https://teste-investidor.tempo.co.mz/bens-de-consumo-franquias",
"status": 200,
"identity_verified": true,
"resource_id": 29,
"document_sha256": "0ba605ae43dec7f685610f828df884bc82cb4da67131fc9783b5cdbe4cc6d9f0",
"current_title": "Bens de Consumo e Franquias",
"image_url": "/documentos/27/view",
"response_sha256": "65145d3b10d494d7e2a8d09736078f9272733c3ae48b5bce501ccffefd63f9bf"
},
"171985": {
"url": "https://teste-investidor.tempo.co.mz/agronegocio",
"status": 200,
"identity_verified": true,
"resource_id": 13,
"document_sha256": "0929184a6d4645b9046512675394ad4f60575273f6dab0955f71cc7474a6a4e2",
"current_title": "Agronegocio",
"image_url": "/documentos/12/view",
"response_sha256": "13c4aeb4307fa5efefa04a9c2cafe0a36b4ab243d93739aa2c1bfe2b0c4ff252"
},
"171981": {
"url": "https://teste-investidor.tempo.co.mz/energia-mineracao",
"status": 200,
"identity_verified": true,
"resource_id": 18,
"document_sha256": "26a70f16cd511c579f08633655ff312db05122f81e44852dd4b163fa6eef85f1",
"current_title": "Energia & Mineração",
"image_url": "/documentos/14/view",
"response_sha256": "10a0e0c00a291929f8923962f9cb31e90bbbb7d2e7eddb856a2ae43e30ca6d1b"
},
"172679": {
"url": "https://teste-investidor.tempo.co.mz/energia",
"status": 200,
"identity_verified": true,
"resource_id": 1,
"document_sha256": "2003755b7dac53b43f4608e707321db6d2f29878926a0e74f4cf27627b70a9cb",
"current_title": "Energia",
"image_url": "/documentos/27/view",
"response_sha256": "41e8ba52174d616cfd81eaa24e4d9135dcfd69a2e6c48e20bfce7b8de5b40166"
},
"172674": {
"url": "https://teste-investidor.tempo.co.mz/mineracao",
"status": 200,
"identity_verified": true,
"resource_id": 2,
"document_sha256": "3ae780f082a8fa7c767635cce98563f5bc027c9656c9f41f8788843a17fa1f54",
"current_title": "Mineração",
"image_url": "/documentos/27/view",
"response_sha256": "b078b87e56f3d68434573aa4d2a2c780845304f44cc65f5f2237732ab32eca7e"
},
"171990": {
"url": "https://teste-investidor.tempo.co.mz/turismo",
"status": 200,
"identity_verified": true,
"resource_id": 19,
"document_sha256": "8cab862be245382dd4c1fd5fcb423c47aed7be3a22e0a7f8d5521510cc36bc72",
"current_title": "Turismo",
"image_url": "/documentos/15/view",
"response_sha256": "471c561e89bf55642ec6aa43b9a27e1dd1f97d0e11e93c8ab95f508b3824da5b"
},
"172263": {
"url": "https://teste-investidor.tempo.co.mz/provincia-da-zambezia-onde-os-corredores-de-mocambique-convergem-com-a-natureza-a-cultura-e-o-turismo-sustentavel",
"status": 200,
"identity_verified": true,
"resource_id": 3,
"document_sha256": "538495cd5b31047ac1c5993fdaf298ec1303330368248b251c87d201d31ff211",
"current_title": "Província da Zambézia: Onde os Corredores de Moçambique Convergem com a Natureza, a Cultura e o Turismo Sustentável",
"image_url": "/documentos/27/view",
"response_sha256": "8629d5647b8ab3f4752ce7452cc131e5b64c0abc06afd25255231a562e0f534e"
},
"172256": {
"url": "https://teste-investidor.tempo.co.mz/provincia-de-sofala-onde-a-natureza-a-historia-e-o-futuro-do-turismo-convergem",
"status": 200,
"identity_verified": true,
"resource_id": 4,
"document_sha256": "dcc29cbd36cf57e0a5c88dd312c84ee03c32a3d18ae9216d3715dc7b5a6960bf",
"current_title": "Província de Sofala: Onde a Natureza, a História e o Futuro do Turismo Convergem",
"image_url": "/documentos/27/view",
"response_sha256": "16f08eaa50b150d73d85ca0b8dccc17ff2b61179813dd6ad0d78ec4309c599d4"
},
"172261": {
"url": "https://teste-investidor.tempo.co.mz/provincia-de-tete-onde-o-rio-zambeze-a-cultura-viva-e-a-conservacao-comunitaria-criam-o-futuro-do-turismo",
"status": 200,
"identity_verified": true,
"resource_id": 5,
"document_sha256": "88177fccd78d8516dba670c6f71272af062cc981d52d97adefe5bf77c613b16a",
"current_title": "Província de Tete: Onde o Rio Zambeze, a Cultura Viva e a Conservação Comunitária Criam o Futuro do Turismo",
"image_url": "/documentos/27/view",
"response_sha256": "7c24f3bfddf8c5bee5ef1312e6d55f46e3ca48c7e48e3f363eac4c95979e6aca"
},
"172258": {
"url": "https://teste-investidor.tempo.co.mz/provincia-de-manica-onde-o-turismo-de-montanha-natureza-e-cultura-encontra-o-proximo-ciclo-de-investimento",
"status": 200,
"identity_verified": true,
"resource_id": 20,
"document_sha256": "48314a576dea2f2dadf47d8f1eadd7ab58030606b02eebe17aa9bd6badffb7e9",
"current_title": "Província de Manica: Onde o Turismo de Montanha, Natureza e Cultura Encontra o Próximo Ciclo de Investimento",
"image_url": "/documentos/27/view",
"response_sha256": "bac797abb94d97ffdc11f8b8cfe9b7782f2c8a9d2311c96e83f71f4ba86bffcb"
},
"172253": {
"url": "https://teste-investidor.tempo.co.mz/provincia-de-inhambane-um-destino-estrategico-para-investimento-turistico",
"status": 200,
"identity_verified": true,
"resource_id": 6,
"document_sha256": "e90e39e49ba65bbbcbbc303c6eca456e445238daf9acc935ab03690819281678",
"current_title": "Província de Inhambane: Um Destino Estratégico para Investimento Turístico",
"image_url": "/documentos/27/view",
"response_sha256": "a6fbbccb7f9499c5aa0034a3d10810a033c6140f1269bd0074e81f1a5bb8da0c"
},
"172240": {
"url": "https://teste-investidor.tempo.co.mz/maputo-cidade-plataforma-estrategica-de-turismo-negocios-e-investimento-sustentavel",
"status": 200,
"identity_verified": true,
"resource_id": 21,
"document_sha256": "8c5e91c937032680883793a12844f1128f7ac99e97c63b68c23d1396e009ba9d",
"current_title": "Maputo Cidade – Plataforma Estratégica de Turismo, Negócios e Investimento Sustentável",
"image_url": "/documentos/27/view",
"response_sha256": "265afdbc2b8997daa69cb519c7409a74d3dd590f7c57b144723786a3c167034d"
},
"172237": {
"url": "https://teste-investidor.tempo.co.mz/provincia-de-maputo-turismo",
"status": 200,
"identity_verified": true,
"resource_id": 22,
"document_sha256": "a62a43e31da32795a7286f4c152a93d5dc829233e72023b00ea61fd8e2fcb58c",
"current_title": "Província de Maputo: Natureza, Cultura e Conectividade como Motores de Crescimento Sustentável",
"image_url": "/documentos/27/view",
"response_sha256": "7827fa353f9fc126938732d42fa57353b48c58f448e25f8466665086779860dc"
},
"172233": {
"url": "https://teste-investidor.tempo.co.mz/provincia-de-gaza-oportunidades-mais-solidas-e-estrategicas-de-investimento-no-turismo-no-sul-de-mocambique",
"status": 200,
"identity_verified": true,
"resource_id": 23,
"document_sha256": "345dfd72d8ebf459eae8eb025d699afddbae751df55ffa8d053568cd08096c97",
"current_title": "Província de Gaza: Oportunidades mais sólidas e estratégicas de investimento no turismo no sul de Moçambique.",
"image_url": "/documentos/27/view",
"response_sha256": "bfb80dff52964448b4bbd03995706020b9bccaa14d8abc1d944123e6b1240a6f"
},
"172230": {
"url": "https://teste-investidor.tempo.co.mz/provincia-de-nampula-perfil-de-investimento-no-setor-do-turismo",
"status": 200,
"identity_verified": true,
"resource_id": 24,
"document_sha256": "4f4570f7bbd5599cb925ed996c1734a23582362fe485736931a53b9266565d4e",
"current_title": "Província de Nampula - Perfil de investimento no setor do turismo",
"image_url": "/documentos/27/view",
"response_sha256": "6d42e0a347c938d201b8c8780fcdf0da83e6dab9fc031ea0ac73b14464f4f696"
},
"172223": {
"url": "https://teste-investidor.tempo.co.mz/cabo-delgado-pemba-a-capital-do-turismo-costeiro-do-norte-de-mocambique",
"status": 200,
"identity_verified": true,
"resource_id": 25,
"document_sha256": "514a9d88c097d234038fa6341f5a5b01e8938cb6a9535a807145c84c37010a4d",
"current_title": "Cabo Delgado - Pemba, A capital do turismo costeiro do norte de Moçambique",
"image_url": "/documentos/27/view",
"response_sha256": "a579796f41716bb43444295dce91b19d0346828e7b3cbfc994efbdc5058ec69b"
},
"172219": {
"url": "https://teste-investidor.tempo.co.mz/niassa-o-ultimo-grande-destino-virgem-do-turismo-africano",
"status": 200,
"identity_verified": true,
"resource_id": 26,
"document_sha256": "0d468ae0de9a76d2b140bc8cb5c555f29626fb6422287c917762bec3e615a70c",
"current_title": "Niassa - O último grande destino virgem do turismo africano",
"image_url": "/documentos/27/view",
"response_sha256": "cffb6ea36fa8273112edaa31432f9c5093ea8e0303fee83c3cdb396e83aac816"
},
"171992": {
"url": "https://teste-investidor.tempo.co.mz/infraestruturas",
"status": 200,
"identity_verified": true,
"resource_id": 17,
"document_sha256": "05eb492dd6cfca6a7f305b633658a16723c315168f4b2c75103f22d1fac84032",
"current_title": "Infraestruturas",
"image_url": "/documentos/16/view",
"response_sha256": "0afd5c42dc0a187a22cb3f8ef8af1b10348d31899ce3b1befbc2c38b379d475b"
}
}
},
"qa_import": {
"port": 55441,
"actor": 239,
"resources": [
{
"id": 43,
"title": "15/30 costa do sol com dua na zona de mupulene muito lindo",
"published": false,
"external_id": "legacy-tempo-post-62413-20170626",
"sha256": "b471c1afd7235f359acdcc16a93088f77e1f988706c8d3c4d4f11c5d560aea47",
"malware_scan_status": "clean",
"malware_scanned_sha256": "b471c1afd7235f359acdcc16a93088f77e1f988706c8d3c4d4f11c5d560aea47",
"data_classification": "internal"
},
{
"id": 44,
"title": "Banco Mundial classifica de proibitivos juros praticados em Moçambique",
"published": false,
"external_id": "legacy-tempo-post-73945-20170731",
"sha256": "2aa0ce0eb305bfdf93e390a978d3694d0fbc0129034077b68cebce418f878415",
"malware_scan_status": "clean",
"malware_scanned_sha256": "2aa0ce0eb305bfdf93e390a978d3694d0fbc0129034077b68cebce418f878415",
"data_classification": "internal"
},
{
"id": 45,
"title": "Coloque o seu número para o grupo de whatsapp, venda de acessórios de computador",
"published": false,
"external_id": "legacy-tempo-post-76474-20170808",
"sha256": "8f769aec4715449d056c8c25294060b2ff290ec0893c28e9bdcc4e8092fc75f5",
"malware_scan_status": "clean",
"malware_scanned_sha256": "8f769aec4715449d056c8c25294060b2ff290ec0893c28e9bdcc4e8092fc75f5",
"data_classification": "internal"
},
{
"id": 46,
"title": "Governo pretende construir 100 mil habitações em dois anos",
"published": false,
"external_id": "legacy-tempo-post-75002-20170804",
"sha256": "7b6fbd998be2b47b96e9a4724560c192c502709fa308d6590c0797ddcdb10544",
"malware_scan_status": "clean",
"malware_scanned_sha256": "7b6fbd998be2b47b96e9a4724560c192c502709fa308d6590c0797ddcdb10544",
"data_classification": "internal"
},
{
"id": 47,
"title": "Rofino Licuco preso cinco meses depois de condenado por agressão",
"published": false,
"external_id": "legacy-tempo-post-73246-20170728",
"sha256": "430ee8674c71104fc126bed2911d676f150ac623857a1ada75662987fd9ac390",
"malware_scan_status": "clean",
"malware_scanned_sha256": "430ee8674c71104fc126bed2911d676f150ac623857a1ada75662987fd9ac390",
"data_classification": "internal"
},
{
"id": 48,
"title": "Sete indivíduos mortos a tiro na Matola",
"published": false,
"external_id": "legacy-tempo-post-77232-20170811",
"sha256": "27544d3127803b7495bef9fd633147d0225d8c31816c4d59ed05a2edd59de070",
"malware_scan_status": "clean",
"malware_scanned_sha256": "27544d3127803b7495bef9fd633147d0225d8c31816c4d59ed05a2edd59de070",
"data_classification": "internal"
},
{
"id": 49,
"title": "Daúde Razaque já não é treinador da Liga Desportiva de Maputo",
"published": false,
"external_id": "legacy-tempo-post-62929-20170627",
"sha256": "488017b062c6760659d9147b7f12aea4afd8d824121adb9462e2c7a2ae58437b",
"malware_scan_status": "clean",
"malware_scanned_sha256": "488017b062c6760659d9147b7f12aea4afd8d824121adb9462e2c7a2ae58437b",
"data_classification": "internal"
},
{
"id": 50,
"title": "Dois alunos da 9ª classe caíram nas mãos da Polícia da República de Moçambique (...",
"published": false,
"external_id": "legacy-tempo-post-63248-20170628",
"sha256": "6c30e61197b8568332af6d9b19bbba7ad8665b3873bb031a8c8aedd41536b060",
"malware_scan_status": "clean",
"malware_scanned_sha256": "6c30e61197b8568332af6d9b19bbba7ad8665b3873bb031a8c8aedd41536b060",
"data_classification": "internal"
},
{
"id": 51,
"title": "Alumínio “Verde”",
"published": false,
"external_id": "legacy-tempo-post-173220-20260616",
"sha256": "5b76c3d84dcf7f9a489686d90e3857ae6536705c918e841310bb42b354e80976",
"malware_scan_status": "clean",
"malware_scanned_sha256": "5b76c3d84dcf7f9a489686d90e3857ae6536705c918e841310bb42b354e80976",
"data_classification": "internal"
},
{
"id": 52,
"title": "Checklist para Contratação e Direitos do Trabalho Doméstico",
"published": false,
"external_id": "legacy-tempo-post-173482-20260915",
"sha256": "6f888e133a779c719073246f89bcae150198e5897c0e7d9b7ba75f54c54a7830",
"malware_scan_status": "clean",
"malware_scanned_sha256": "6f888e133a779c719073246f89bcae150198e5897c0e7d9b7ba75f54c54a7830",
"data_classification": "internal"
},
{
"id": 53,
"title": "Frelimo e Zanu-PF: Evocados laços de irmandade",
"published": false,
"external_id": "legacy-tempo-post-62414-20170626",
"sha256": "a040c0b7cb95dd7f053f4d13662dd58b2b06453b6fa61a2d531d0257a8db8c17",
"malware_scan_status": "clean",
"malware_scanned_sha256": "a040c0b7cb95dd7f053f4d13662dd58b2b06453b6fa61a2d531d0257a8db8c17",
"data_classification": "internal"
},
{
"id": 54,
"title": "Governo dará todo O apoio à PGR no seguimento da auditoria",
"published": false,
"external_id": "legacy-tempo-post-62418-20170626",
"sha256": "e8ccafa68f0d1aa80f2e41d9e09f7b13e7c653a8472a731798cfd5e04ce2963e",
"malware_scan_status": "clean",
"malware_scanned_sha256": "e8ccafa68f0d1aa80f2e41d9e09f7b13e7c653a8472a731798cfd5e04ce2963e",
"data_classification": "internal"
},
{
"id": 55,
"title": "Sete pessoas morrem e 150 são resgatados em acidente com eléctrico na Índia",
"published": false,
"external_id": "legacy-tempo-post-62416-20170626",
"sha256": "ac575ce340571192552c89b63f0bd62a84dbc47828e6930133af5ff70e249319",
"malware_scan_status": "clean",
"malware_scanned_sha256": "ac575ce340571192552c89b63f0bd62a84dbc47828e6930133af5ff70e249319",
"data_classification": "internal"
},
{
"id": 56,
"title": "Daviz Simango não se incomoda com possível sucessão na Beira",
"published": false,
"external_id": "legacy-tempo-post-62649-20170626",
"sha256": "38a858713472759a2d20a308bdd9d8afa9cfb76dcaf8d11ca709e89261573e42",
"malware_scan_status": "clean",
"malware_scanned_sha256": "38a858713472759a2d20a308bdd9d8afa9cfb76dcaf8d11ca709e89261573e42",
"data_classification": "internal"
},
{
"id": 57,
"title": "“Exigimos responsabilização dos agentes do crime”, Daviz Simango",
"published": false,
"external_id": "legacy-tempo-post-62739-20170626",
"sha256": "5870fc0fad9bb7af537b7b1468151cde138b1b886ce846c7be25647b630245dd",
"malware_scan_status": "clean",
"malware_scanned_sha256": "5870fc0fad9bb7af537b7b1468151cde138b1b886ce846c7be25647b630245dd",
"data_classification": "internal"
},
{
"id": 58,
"title": "Governo e parceiros reforçam mecanismos de controlo de HIV/Sida",
"published": false,
"external_id": "legacy-tempo-post-62854-20170627",
"sha256": "44b1ba9d593bb625cf53de8909e78eb0b3129742c3ec9d58ec68c984e8930bce",
"malware_scan_status": "clean",
"malware_scanned_sha256": "44b1ba9d593bb625cf53de8909e78eb0b3129742c3ec9d58ec68c984e8930bce",
"data_classification": "internal"
},
{
"id": 59,
"title": "Graça Machel diz que desnutrição é um problema de desenvolvimento",
"published": false,
"external_id": "legacy-tempo-post-63128-20170627",
"sha256": "74be8f29e5053f53ab1a39a2f63208145c30f23357ca6d78b637dd02bf9c1fb4",
"malware_scan_status": "clean",
"malware_scanned_sha256": "74be8f29e5053f53ab1a39a2f63208145c30f23357ca6d78b637dd02bf9c1fb4",
"data_classification": "internal"
},
{
"id": 60,
"title": "In dependência",
"published": false,
"external_id": "legacy-tempo-post-63181-20170627",
"sha256": "3d0f9ac14d36156d8178e33f8616d6406f31ea4cd69c8308cbf4227586d7ccb3",
"malware_scan_status": "clean",
"malware_scanned_sha256": "3d0f9ac14d36156d8178e33f8616d6406f31ea4cd69c8308cbf4227586d7ccb3",
"data_classification": "internal"
},
{
"id": 61,
"title": "Kroll não descobriu como foram gastos 2 biliões de dólares; Governo de Nyusi não apoiou a Auditoria; criação da Proindicus, EMATUM e MAM não foi para proteção da Zona Económica Exclusiva",
"published": false,
"external_id": "legacy-tempo-post-63146-20170627",
"sha256": "542353466cc6d82b651619c495f138b0576a4be41d21aad194ae2005050cb1bc",
"malware_scan_status": "clean",
"malware_scanned_sha256": "542353466cc6d82b651619c495f138b0576a4be41d21aad194ae2005050cb1bc",
"data_classification": "internal"
},
{
"id": 62,
"title": "O Instituto Nacional de Meteorologia prevê o seguinte estado do tempo para esta ...",
"published": false,
"external_id": "legacy-tempo-post-62688-20170626",
"sha256": "a210a789b06d425822f4124f74226a2b27246c086c5ec5f041f1b2237d7c2b65",
"malware_scan_status": "clean",
"malware_scanned_sha256": "a210a789b06d425822f4124f74226a2b27246c086c5ec5f041f1b2237d7c2b65",
"data_classification": "internal"
},
{
"id": 63,
"title": "Energia e Recursos Naturais",
"published": false,
"external_id": "legacy-tempo-post-173222-20260616",
"sha256": "e64d7063d1ac4dda028c97e1ba64938d2a9e426a0b630c57ee5df52a41312cc7",
"malware_scan_status": "clean",
"malware_scanned_sha256": "e64d7063d1ac4dda028c97e1ba64938d2a9e426a0b630c57ee5df52a41312cc7",
"data_classification": "internal"
},
{
"id": 64,
"title": "Direitos Especiais da Trabalhadora Doméstica no Regulamento",
"published": false,
"external_id": "legacy-tempo-post-173484-20260915",
"sha256": "297df455a9c6fd1f0ab7ca7bbe124ce483c86fe55be26baa695f03089b3ac049",
"malware_scan_status": "clean",
"malware_scanned_sha256": "297df455a9c6fd1f0ab7ca7bbe124ce483c86fe55be26baa695f03089b3ac049",
"data_classification": "internal"
},
{
"id": 65,
"title": "Guia Prático para Empregadores sobre Contratação e Direitos no Trabalho Doméstico",
"published": false,
"external_id": "legacy-tempo-post-173478-20260915",
"sha256": "d4e320ed99dd9d53d205e47932f73651976d43081d5d39f2bfc2996901408644",
"malware_scan_status": "clean",
"malware_scanned_sha256": "d4e320ed99dd9d53d205e47932f73651976d43081d5d39f2bfc2996901408644",
"data_classification": "internal"
},
{
"id": 66,
"title": "Modelo de Contrato de Trabalho Doméstico para Pequenas Famílias",
"published": false,
"external_id": "legacy-tempo-post-173480-20260915",
"sha256": "c3ee6401eda5b582c61969b5e3b74fdf26ef34ca481b1aca8c4c309d845147ae",
"malware_scan_status": "clean",
"malware_scanned_sha256": "c3ee6401eda5b582c61969b5e3b74fdf26ef34ca481b1aca8c4c309d845147ae",
"data_classification": "internal"
},
{
"id": 67,
"title": "Procedimentos para Rescisão do Contrato de Trabalho Doméstico segundo o Regulamento de Moçambique",
"published": false,
"external_id": "legacy-tempo-post-173476-20260915",
"sha256": "72fbf28391db4804e0637b64e7f59e9fa597812a2681b4562dc3d6dfb4093a30",
"malware_scan_status": "clean",
"malware_scanned_sha256": "72fbf28391db4804e0637b64e7f59e9fa597812a2681b4562dc3d6dfb4093a30",
"data_classification": "internal"
},
{
"id": 68,
"title": "Direitos e Deveres do Trabalhador Doméstico",
"published": false,
"external_id": "legacy-tempo-post-173470-20260915",
"sha256": "26feeae6b8d04972c7a64ee360583cccdcaeb4b0d8795fd7bc2fd3c38866cc19",
"malware_scan_status": "clean",
"malware_scanned_sha256": "26feeae6b8d04972c7a64ee360583cccdcaeb4b0d8795fd7bc2fd3c38866cc19",
"data_classification": "internal"
},
{
"id": 69,
"title": "Sanções Disciplinares Aplicáveis a Trabalhadores Domésticos segundo o Regulamento de Moçambique",
"published": false,
"external_id": "legacy-tempo-post-173474-20260915",
"sha256": "9fa741a17b746bfa2f3ce7b6ce1d2bb34d3a35d3b58330b0554f9e0a6844f049",
"malware_scan_status": "clean",
"malware_scanned_sha256": "9fa741a17b746bfa2f3ce7b6ce1d2bb34d3a35d3b58330b0554f9e0a6844f049",
"data_classification": "internal"
},
{
"id": 70,
"title": "Regulamento do Trabalho Doméstico",
"published": false,
"external_id": "legacy-tempo-post-173463-20260915",
"sha256": "4f34b7bac4b3030c0614844d379f6e3cfad1b7f6ea3db575a58c33ae033b0e73",
"malware_scan_status": "clean",
"malware_scanned_sha256": "4f34b7bac4b3030c0614844d379f6e3cfad1b7f6ea3db575a58c33ae033b0e73",
"data_classification": "internal"
}
],
"imported_private": 28,
"clean_actual_scans": 28,
"idempotent_replay_reused": 28,
"existing_resources_preserved": true,
"roles_unchanged": true,
"new_publications": 0,
"active_writes": 0
},
"original_gaps": [
{
"legacy_id": 171994,
"url": "https://investemoz.tempo.co.mz/mercado-financeiro",
"status": 200,
"title": "Mercado Financeiro",
"body_characters": 0,
"body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"same_body_as_capture": true
},
{
"legacy_id": 74364,
"url": "https://investemoz.tempo.co.mz/74364-2",
"status": 200,
"title": "",
"body_characters": 274,
"body_sha256": "2a7f0a5b09e0136fec9eae357ac8a88a159ead23b25ed7bb735c75997b10ec5e",
"same_body_as_capture": true
}
],
"educational_cards": {
"in_main": true,
"observed_url": "https://teste-investidor.tempo.co.mz/explorar/turismo",
"read_guide_links": 12,
"visual_acceptance_by_tempo": false
}
}
@@ -0,0 +1,339 @@
{
"qa_only": true,
"originals": 28,
"rows": [
{
"external_id": "legacy-tempo-post-62413-20170626",
"qa_resource_id": 43,
"version": 2,
"original_sha256": "b471c1afd7235f359acdcc16a93088f77e1f988706c8d3c4d4f11c5d560aea47",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 2
},
{
"external_id": "legacy-tempo-post-73945-20170731",
"qa_resource_id": 44,
"version": 1,
"original_sha256": "2aa0ce0eb305bfdf93e390a978d3694d0fbc0129034077b68cebce418f878415",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-76474-20170808",
"qa_resource_id": 45,
"version": 1,
"original_sha256": "8f769aec4715449d056c8c25294060b2ff290ec0893c28e9bdcc4e8092fc75f5",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 2,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-62414-20170626",
"qa_resource_id": 53,
"version": 1,
"original_sha256": "a040c0b7cb95dd7f053f4d13662dd58b2b06453b6fa61a2d531d0257a8db8c17",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-62418-20170626",
"qa_resource_id": 54,
"version": 1,
"original_sha256": "e8ccafa68f0d1aa80f2e41d9e09f7b13e7c653a8472a731798cfd5e04ce2963e",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-75002-20170804",
"qa_resource_id": 46,
"version": 1,
"original_sha256": "7b6fbd998be2b47b96e9a4724560c192c502709fa308d6590c0797ddcdb10544",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-73246-20170728",
"qa_resource_id": 47,
"version": 1,
"original_sha256": "430ee8674c71104fc126bed2911d676f150ac623857a1ada75662987fd9ac390",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-77232-20170811",
"qa_resource_id": 48,
"version": 1,
"original_sha256": "27544d3127803b7495bef9fd633147d0225d8c31816c4d59ed05a2edd59de070",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-62416-20170626",
"qa_resource_id": 55,
"version": 1,
"original_sha256": "ac575ce340571192552c89b63f0bd62a84dbc47828e6930133af5ff70e249319",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-62929-20170627",
"qa_resource_id": 49,
"version": 1,
"original_sha256": "488017b062c6760659d9147b7f12aea4afd8d824121adb9462e2c7a2ae58437b",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-62649-20170626",
"qa_resource_id": 56,
"version": 1,
"original_sha256": "38a858713472759a2d20a308bdd9d8afa9cfb76dcaf8d11ca709e89261573e42",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-62739-20170626",
"qa_resource_id": 57,
"version": 1,
"original_sha256": "5870fc0fad9bb7af537b7b1468151cde138b1b886ce846c7be25647b630245dd",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-62854-20170627",
"qa_resource_id": 58,
"version": 1,
"original_sha256": "44b1ba9d593bb625cf53de8909e78eb0b3129742c3ec9d58ec68c984e8930bce",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-63128-20170627",
"qa_resource_id": 59,
"version": 1,
"original_sha256": "74be8f29e5053f53ab1a39a2f63208145c30f23357ca6d78b637dd02bf9c1fb4",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-63181-20170627",
"qa_resource_id": 60,
"version": 1,
"original_sha256": "3d0f9ac14d36156d8178e33f8616d6406f31ea4cd69c8308cbf4227586d7ccb3",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-63146-20170627",
"qa_resource_id": 61,
"version": 1,
"original_sha256": "542353466cc6d82b651619c495f138b0576a4be41d21aad194ae2005050cb1bc",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 1,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-62688-20170626",
"qa_resource_id": 62,
"version": 1,
"original_sha256": "a210a789b06d425822f4124f74226a2b27246c086c5ec5f041f1b2237d7c2b65",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-63248-20170628",
"qa_resource_id": 50,
"version": 1,
"original_sha256": "6c30e61197b8568332af6d9b19bbba7ad8665b3873bb031a8c8aedd41536b060",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173220-20260616",
"qa_resource_id": 51,
"version": 1,
"original_sha256": "5b76c3d84dcf7f9a489686d90e3857ae6536705c918e841310bb42b354e80976",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173222-20260616",
"qa_resource_id": 63,
"version": 1,
"original_sha256": "e64d7063d1ac4dda028c97e1ba64938d2a9e426a0b630c57ee5df52a41312cc7",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173482-20260915",
"qa_resource_id": 52,
"version": 1,
"original_sha256": "6f888e133a779c719073246f89bcae150198e5897c0e7d9b7ba75f54c54a7830",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173484-20260915",
"qa_resource_id": 64,
"version": 1,
"original_sha256": "297df455a9c6fd1f0ab7ca7bbe124ce483c86fe55be26baa695f03089b3ac049",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173478-20260915",
"qa_resource_id": 65,
"version": 1,
"original_sha256": "d4e320ed99dd9d53d205e47932f73651976d43081d5d39f2bfc2996901408644",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173480-20260915",
"qa_resource_id": 66,
"version": 1,
"original_sha256": "c3ee6401eda5b582c61969b5e3b74fdf26ef34ca481b1aca8c4c309d845147ae",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173476-20260915",
"qa_resource_id": 67,
"version": 1,
"original_sha256": "72fbf28391db4804e0637b64e7f59e9fa597812a2681b4562dc3d6dfb4093a30",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173470-20260915",
"qa_resource_id": 68,
"version": 1,
"original_sha256": "26feeae6b8d04972c7a64ee360583cccdcaeb4b0d8795fd7bc2fd3c38866cc19",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173474-20260915",
"qa_resource_id": 69,
"version": 1,
"original_sha256": "9fa741a17b746bfa2f3ce7b6ce1d2bb34d3a35d3b58330b0554f9e0a6844f049",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
},
{
"external_id": "legacy-tempo-post-173463-20260915",
"qa_resource_id": 70,
"version": 1,
"original_sha256": "4f34b7bac4b3030c0614844d379f6e3cfad1b7f6ea3db575a58c33ae033b0e73",
"private": true,
"image_available": false,
"image_reference": "https://investemoz.tempo.co.mz/images/tempo-background.png",
"body_image_references": 0,
"history_revisions": 1
}
],
"archive_entries": 57,
"archive_sha256": "177c50d1cc3b1610e62123a2f8b532711a15f875aac2ee0e372636c2f747e8a2",
"archive_bytes": 85614,
"verified_original_hashes": 28,
"included_image_originals": 0,
"destination_imported": false,
"destination_published": false,
"database_writes": 0,
"tests": {
"passed": 6,
"assertions": 1000,
"database": "simulator_fiscal_delta_20261021_test",
"port": 55441,
"publication_transactions_rolled_back": true,
"all_28_sanitized_original_text_preserved": true
},
"typescript": "passed",
"qa_build": "passed",
"public20_rechecked": false,
"reimported": false,
"main_integrated": false,
"user_review_claimed": false,
"ui_saved_resource": 43,
"ui_saved_version": 2,
"original_image_sha256": "28f41f12e10ab34545d8e432f6e8c74f86960e17e0c4a9956f514bcde4881eaa"
}
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 MiB

Some files were not shown because too many files have changed in this diff Show More