From 6b544548d3c83fc8a72ab65d8f58f84685a2877f Mon Sep 17 00:00:00 2001 From: nala Date: Fri, 23 Oct 2026 15:41:37 +0200 Subject: [PATCH] Fix compressed draft saves provenance and Beira documentary continuity --- .../InvestmentDiscoveryController.php | 2 +- .../InvestmentPortfolioController.php | 1 + .../CorridorAgribusinessOpportunities.php | 11 ++++++ app/Services/InvestmentCatalogue.php | 4 +++ app/Services/InvestmentOpportunityChoices.php | 1 + app/Services/InvestorContext.php | 1 + .../unification/traceability-and-manual.md | 12 +++++++ .../v2/auditoria-portal/matriz-requisitos.csv | 6 ++-- resources/js/components/simulation-form.tsx | 17 ++++----- resources/js/lib/context-inheritance.ts | 20 +++++++++++ resources/js/lib/project-dossier-report.ts | 4 +-- resources/js/lib/project-dossier.ts | 7 +++- resources/js/lib/simulation-draft-etag.ts | 5 +++ .../InvestmentPortfolioContinuityTest.php | 35 +++++++++++++++++++ .../frontend/investment-field-origin.test.mjs | 22 ++++++++++++ tests/frontend/simulation-draft-etag.test.mjs | 12 +++++++ 16 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 resources/js/lib/simulation-draft-etag.ts create mode 100644 tests/frontend/investment-field-origin.test.mjs create mode 100644 tests/frontend/simulation-draft-etag.test.mjs diff --git a/app/Http/Controllers/InvestmentDiscoveryController.php b/app/Http/Controllers/InvestmentDiscoveryController.php index ca1f0360..86cec5e0 100644 --- a/app/Http/Controllers/InvestmentDiscoveryController.php +++ b/app/Http/Controllers/InvestmentDiscoveryController.php @@ -89,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; diff --git a/app/Http/Controllers/InvestmentPortfolioController.php b/app/Http/Controllers/InvestmentPortfolioController.php index d127ee21..985525f7 100644 --- a/app/Http/Controllers/InvestmentPortfolioController.php +++ b/app/Http/Controllers/InvestmentPortfolioController.php @@ -105,6 +105,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')); diff --git a/app/Services/CorridorAgribusinessOpportunities.php b/app/Services/CorridorAgribusinessOpportunities.php index 1b6138f2..bc9d90d5 100644 --- a/app/Services/CorridorAgribusinessOpportunities.php +++ b/app/Services/CorridorAgribusinessOpportunities.php @@ -13,6 +13,17 @@ use Ramsey\Uuid\Uuid; 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 diff --git a/app/Services/InvestmentCatalogue.php b/app/Services/InvestmentCatalogue.php index c6160869..a7990dc0 100644 --- a/app/Services/InvestmentCatalogue.php +++ b/app/Services/InvestmentCatalogue.php @@ -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; } diff --git a/app/Services/InvestmentOpportunityChoices.php b/app/Services/InvestmentOpportunityChoices.php index 1e2f05d9..202b233d 100644 --- a/app/Services/InvestmentOpportunityChoices.php +++ b/app/Services/InvestmentOpportunityChoices.php @@ -15,6 +15,7 @@ final class InvestmentOpportunityChoices 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'); } diff --git a/app/Services/InvestorContext.php b/app/Services/InvestorContext.php index bf201328..c27e0cb3 100644 --- a/app/Services/InvestorContext.php +++ b/app/Services/InvestorContext.php @@ -632,6 +632,7 @@ final class InvestorContext '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.', diff --git a/docs/investmoz/unification/traceability-and-manual.md b/docs/investmoz/unification/traceability-and-manual.md index a1239c34..146235f9 100644 --- a/docs/investmoz/unification/traceability-and-manual.md +++ b/docs/investmoz/unification/traceability-and-manual.md @@ -1,5 +1,17 @@ # Rastreabilidade, utilização e formação +## 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). diff --git a/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv b/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv index 36c51a0d..5673c7f1 100644 --- a/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv +++ b/docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv @@ -10653,9 +10653,9 @@ AUD-SEARCH-T11,Consistência transversal da pesquisa e navegação — Legislaç AUD-GUIDE-CARD-20261020,"Related educational cards: image, source summary, clear actions",Tempo 20/10/2026; independent delta after 0e7d51f18fb4,educational presentation,/sectores/{slug}; /explorar/{group}; articles; /oportunidades and detail,EducationalArticleCard; EducationalGuides; LegacyContent; SectorInvestment,QA only,1 test / 8 assertions; PHP syntax; TypeScript/build; desktop2048/mobile390; keyboard,QA review pending,Normal QA sector and article verified against existing library resource 10 and local image 25; previous dossier proof retained,,Source-derived summary; public images; conditional opportunity action; context preserved,P1,,,,,,,,,,,,,,,,,,presentation,,,,20/10/2026,,,,,,,,,,,,content; navigation; visual presentation,,Ready for QA review AUD-SIM-INT01,Invalid one-off fiscal date can throw instead of returning validation state,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Fixed in QA; invalid-date checks; 38 directed frontend tests passed,Previous inline-date proof retained; unchanged delta not repeated,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,, | [Saldo consolidado21/10/2026] Concluído no recorte. Nenhum delta de validação de data por demonstrar; prova anterior preservada. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.,,20/10/2026,,,,,,,,,,,,,, AUD-SIM-INT02,Review actual fiscal parameters sources periods and eligibility,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,"QA only | 22/10/2026 — disponibilizado por autorização Tempo: revisão servida042ffbde (inclui a76369eb/a1fbda06/064296e1 e correcção dirigida da recuperação legada). Percurso habilitado na homologação, versão private-investment-preparation-2026-v1; main ancestral preservado, sem conflitos/force-push. Zero migrations/importações/fixtures; nenhuma nova regra fiscal publicada/activada. Revisão/configuração/rotas conferidas; provas funcionais QA reutilizadas.","Verification pending; no parameter activation | 22/10/2026: primeiro incremento QA na branch codex/project-context-continuity-QA; contexto único por dossier/conta, herança com edições preservadas, moeda/IVA mensal e análise territorial explícita. Ver traceability-and-manual.md, secção Continuidade e adaptação contextual. 4 HTTP/74 asserções; 8 frontend; percurso visual A/B e nova sessão. Não encerra adaptação jurídica integral. | 22/10/2026 — percurso privado executável em QA (base52fbb146): condições oficiais seleccionam mero registo/autorização, alternativas documentais e próximos passos; contexto C v4 mero registo após distinção directo/indirecto (Lei14/R13); prévia visual, vínculo inactivo preservado e relatório v3 por autorização conservado como histórico. 3 testes/96 asserções e um teste do novo relatório, tipos/build aprovados. Sem validação jurídica genérica adicional; orientação permanente Tempo em AGENTS. Branch codex/private-investment-path-executable-QA; sem integração main/alteração da homologação/activação fiscal.",Recorte IVA geral sobre base declarada disponibilizado: 4a4d1212 integrado na revisao servida d3fe8a02; regra real v1 publicada e consumidor activo. Nao conclui o simulador fiscal integral.,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P0,,,,,,,,,,,,,"22/10/2026: primeiro incremento QA na branch codex/project-context-continuity-QA; contexto único por dossier/conta, herança com edições preservadas, moeda/IVA mensal e análise territorial explícita. Ver traceability-and-manual.md, secção Continuidade e adaptação contextual. 4 HTTP/74 asserções; 8 frontend; percurso visual A/B e nova sessão. Não encerra adaptação jurídica integral. | 22/10/2026 — percurso privado executável em QA (base52fbb146): condições oficiais seleccionam mero registo/autorização, alternativas documentais e próximos passos; contexto C v4 mero registo após distinção directo/indirecto (Lei14/R13); prévia visual, vínculo inactivo preservado e relatório v3 por autorização conservado como histórico. 3 testes/96 asserções e um teste do novo relatório, tipos/build aprovados. Sem validação jurídica genérica adicional; orientação permanente Tempo em AGENTS. Branch codex/private-investment-path-executable-QA; sem integração main/alteração da homologação/activação fiscal.","Nenhuma operacao de disponibilizacao pendente neste lote delimitado. Outras componentes, regimes e coberturas fiscais permanecem fora do recorte; nao repetir provas aceites. | Recorte executável C concluído em QA. Restantes coberturas sectoriais/benefícios e encaminhamento visual de zona real têm limites concretos no manual; não bloqueiam os ramos já documentados. Integração/habilitação no destino não executadas. | Integração/habilitação deste recorte executadas em22/10/2026; referências anteriores a operação pendente são históricas. Não reabrir as provas aceites.",,,"Tempo autorizou incorporacao de 4a4d1212, cadastro/publicacao da regra real e activacao. Mesma administracao real nala@tempo.co.mz, login normal e MFA, sem quatro designacoes; sem parecer juridico ou revisao independente atribuidos a Tempo.",Ligacao de consumidor publicada concluida em QA; decisoes operacionais separadas.,Recorte executado; sem saldo de implementacao ou disponibilizacao identificado neste lote. Nao converter estimativas anteriores em implementacao fiscal integral.,"[Leitura SSH 21/10/2026 08:17 Maputo] Zero regras em simulation_tax_rule_versions na homologação, incluindo todos os estados por leitura administrativa sem ocultação RLS. Seis referências em investment_legal_sources; não são parâmetros executáveis. QA também tinha zero regras. BEGIN READ ONLY/SELECT/ROLLBACK; zero escritas. Prova homologation-fiscal-readonly.json. O git HEAD registado é do checkout consultado, não certifica a revisão do build servido. A ausência de cadastro não é ausência de fontes oficiais nem reabre a aceitação da Tempo de 08/10. Não implementa a ligação funcional ao motor fiscal. | [Saldo consolidado21/10/2026] Parcial. Leitura remota concluída: zero regras de todos os estados na homologação, tal como QA; seis referências jurídicas não são parâmetros executáveis. Falta cadastro fundamentado por componente/âmbito/período e ligação funcional ao motor fiscal. Fontes e análises existentes reutilizáveis; aceitação oficial da Tempo de08/10 preservada. Não activar taxas nem importar/publicar por esta reconciliação. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Candidata21/10/2026] Diagnóstico fiscal separado no manual existente: fontes/analises já disponíveis; zero parâmetros executáveis na QA e homologação por leitura de todos os estados; governação/resolvedor temporal/DSL existentes; RunSimulation ainda aritmética personalizada sem consumir regras fiscais. Saldo: cadastro fundamentado, orquestração, contrato de resultado/histórico e ligação ao dossier/relatório, com casos por componente/período. Nenhuma regra criada nem activada; implementação fiscal integral aberta e excluída das1–2h de exportações. | [Proposta21/10/2026] Primeiro lote proposto: uma regra IVA geral sobre base liquida/MZN, cobertura demonstrada2026 sem inventar caducidade legal. Reutilizar fontes/analises/governacao/resolvedor/avaliador existentes; arredondamento e ambito aplicavel explicitamente por fundamentar antes de publicar. Cadastro4-8h e codigo/aceitacao12-20h, total16-28h condicionado ao acervo, separado das1-2h de exportacao. Criterios e exclusoes no manual existente. Nenhuma regra cadastrada/activada; nao e implementacao fiscal integral. | [QA IVA v1.2 21/10/2026] Orientacao posterior Tempo: calcular com precisao decimal exacta sem arredondamento automatico, separando apresentacao monetaria; arredondamento generico deixa de ser bloqueio deste lote. Cadastro draft versionado em clone QA; resolvedor/avaliador existentes; proveniencia, historico e relatorio imutavel ligados. 5 feature (51 assercoes), 7 unit (25), 3 frontend; types/build/Pint aprovados. Contexto declarado; nao e saldo a pagar. Zero regras publicadas; sem main/activacao. Recorte QA autorizado verificado (0h engenharia identificada restante); produto fiscal integral continua parcial. Limites e provas no mesmo manual e ficha v1.2; sem atribuicao de validacao juridica ao utilizador. | [Revisao 83d2f97d] Ambito/periodo/exclusoes e procedimentos de integracao, publicacao, activacao e suspensao consolidados no mesmo manual. Lacuna concreta de ecra QA fechada: entradas 1000.03/MZN/2026-10-21; calculo exacto 160.0048; gravacao fa6d17f2-5f47-4cb6-b8cb-40850c12505f; recarga; recuperacao do mesmo ID; relatorio d13e2ad5-3ba8-4d01-9d30-5815496da774 com mesmo valor/proveniencia e hash igual ao resultado. Uma execucao persistida, draft e zero regras publicadas. 1 teste novo de rota/17 assercoes, TypeScript/build e browser aprovados; restantes provas reutilizadas. Interrupcao inicial da ferramenta resolvida, sem atribuicao de falha ao simulador. Revisao do recorte demonstrada; sem nova preparacao generica identificada, main ou activacao; nao fecha aplicabilidade fiscal universal nem simulador integral. | 21/10/2026: proposta do consumidor IVA no simulador existente no manual: falta ligacao a regra publicada e activacao explicita. Integrar codigo desligado, publicar versao e activar consumidor sao tres operacoes distintas. Reutiliza permissoes, governanca, historico e provas aceites; sem ampliar cobertura ou executar estas operacoes. | 21/10/2026: implementado delta de 2ced7b79 no simulador normal QA, activacao desligada por omissao. 8 testes/75 assercoes, 2 testes frontend novos, TypeScript/build/Pint. Edge: indeterminacao, calculo, repeticao sem duplicar, fila admitida antes de suspensao concluida depois, nova execucao recusada e historicos/relatorios preservados. Mesmo manual e recibo consolidam provas e tres operacoes. Fixture isolada suspensa; sem merge ou publicacao/activacao na homologacao. | 21/10/2026: origem da condicao fiscal localizada em e65cb269/servico/CHECKs de 03/10 e nota de implementacao; sem decisao expressa aplicavel encontrada. Correccao QA: administracao existente, mesma pessoa, fonte oficial identificada e eventos preservados. 6 testes/86 assercoes apenas do controlo, sem calculos/exportadores; constraints de outras tabelas iguais. Esquema activo nao alterado; backup/restauro de 171 tabelas confirmado; sem fixture ou publicacao real. | [Execucao autorizada 21/10/2026 10:56 UTC] Servido d3fe8a02ec7f5dcd01984c4f4a1eba18b5a0cf19 contendo 4a4d1212; DDL fiscal aplicado autenticadamente com backup/restauro de171tabelas e registos preservados. Tres fontes reais v1 com hashes/citacoes e aceitacao fiscal Tempo08/10/2026, sem validacao geral automatica. Regra IVA-GERAL-BASE-LIQUIDA rule_version1, ID01a15e1a-5338-700f-8a45-f87707d696a7, published, version_no5, actor15 emtodasetapas;5eventos. Activacao separada ligada a esseID/versao; APIHTTP200 enabled=true e resolvedor16percent no contexto delimitado sem calculo novo. FixtureQA nao importada; calculos/exportadores/PDF aceites nao repetidos. Suspensao de novas admissoes conserva snapshots/historicos/relatorios. Recibo e manual existentes actualizados; limitacoes de abertura de consola/extensao registadas como ferramenta. | [Diagnostico IVA QA21/10/2026] Falha estrutural reproduzida na consulta anual: fiscal_operation nao satisfaz operation e moeda nao era comunicada. Sem presumir respostas originais. Cada condicao tem campo/recebido/esperado/categoria; notas junto ao campo, resumo com foco e Ver fundamento semDSL/codigo. Moeda fiscal explicita, sem conversao/copia.5unitarios24assercoes+1API16assercoes, Edge comUSD dirigido preservado apos recuperacao; fixtureQA v3suspensa, consumidorQAoff. Nao altera regra/activacao da homologacao; calculos e exportadores nao repetidos. Branch codex/iva-diagnostics-QA. | [IVA mensal QA21/10/2026] Vendas por produto/mes reutilizadas; base liquida explicita; versao publicada seleccionada no servidor, sem taxa/manual no formulario. Zero distinto de falta; preco com IVA indeterminado. Actualizacao dependente cria versao, historicos/relatorios preservados. IVA separado de recebimentos/custos/imposto final; manual apenas cenario personalizado. 3API/46assercoes+4frontend e Edge: vendas alteradas, bruto, zero, falta, recuperacao e relatorio. Fixture sintetica v4 ligada apenas no clone QA para revisao. Sem main/HML ou alargamento fiscal; provas anteriores reutilizadas. Mesmo manual/recibo; branch codex/iva-monthly-projection-QA. | [Modelo A QA21/10/2026] Exemplar Tempo e imagem incorporada conferidos/preservados privados na biblioteca QA, hashes/scans/idempotencia; quadros e campos01-27 mapeados no mesmo registo. Componente01/02 existente; apuramento14-18 indeterminado. Correspondencia consultavel e compras/encomendas reutilizadas sem fabricar base/IVA/deducao. Divergencias de remissao registadas; edicao vigente nao afirmada.3testes novos/types/build/Edge; calculo/exportadores aceites reutilizados. Delta seguinte8-16h cadastro+20-36h codigo, condicionado e sem creditos/substituicao/pagamento/reembolso; nao e cobertura integral. Sem main/HML, nova regra publicada ou activacao. Ver docs/investmoz/simulations/modelo-a-reconciliacao-20261021.md. | 22/10/2026: primeiro incremento QA na branch codex/project-context-continuity-QA; contexto único por dossier/conta, herança com edições preservadas, moeda/IVA mensal e análise territorial explícita. Ver traceability-and-manual.md, secção Continuidade e adaptação contextual. 4 HTTP/74 asserções; 8 frontend; percurso visual A/B e nova sessão. Não encerra adaptação jurídica integral. | 22/10: revisão de a76369eb concluída no âmbito aceite por prova equivalente do regresso; A/B não repetidos. Tabela do primeiro percurso de investimento privado no manual: condições, artigos/período e efeitos; preparação documental, sem ligação executável nova. | 22/10/2026 — percurso privado executável em QA (base52fbb146): condições oficiais seleccionam mero registo/autorização, alternativas documentais e próximos passos; contexto C v4 mero registo após distinção directo/indirecto (Lei14/R13); prévia visual, vínculo inactivo preservado e relatório v3 por autorização conservado como histórico. 3 testes/96 asserções e um teste do novo relatório, tipos/build aprovados. Sem validação jurídica genérica adicional; orientação permanente Tempo em AGENTS. Branch codex/private-investment-path-executable-QA; sem integração main/alteração da homologação/activação fiscal. | 22/10/2026 — prova complementar exclusiva do relatório: recuperação UI do contexto v4, rascunho v5 e Word novo por mero registo; estudo/capacidade não aplicáveis. Word v3 preservado/identificado como histórico, hash inalterado. Sem alteração funcional ou repetição dos ensaios aceites. Dependências Git: a76369eb → 52fbb146 documental → a1fbda06; integrar após decisão, preservando main concorrente. | 22/10/2026 — disponibilizado por autorização Tempo: revisão servida042ffbde (inclui a76369eb/a1fbda06/064296e1 e correcção dirigida da recuperação legada). Percurso habilitado na homologação, versão private-investment-preparation-2026-v1; main ancestral preservado, sem conflitos/force-push. Zero migrations/importações/fixtures; nenhuma nova regra fiscal publicada/activada. Revisão/configuração/rotas conferidas; provas funcionais QA reutilizadas.",,20/10/2026,,,,,,,,,,"Catálogo visual sintético QA; ZEE/ZFI apenas controlos HTTP revertidos; condições/benefícios não executáveis indeterminados; sem ensaio visual integral dos outros consumidores. | C exclusivamente sintético; ZEE/ZFI encaminhamento por predicados, sem reconhecimento de zona real; download concluído apesar de perda da ferramenta. Provas A/B/cálculos/exportadores aceites não repetidas.",,,,"Recorte adaptativo de preparação disponibilizado/habilitado na homologação, revisão042ffbde. Limitações globais/sectoriais/fiscais mantêm âmbito próprio." -AUD-SIM-INT03,Review isolation recovery across projects and sessions,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing controls and accepted proofs to reconcile; no leak established,Two dossiers and new login recovered in browser; cashflow persistence corrected and HTTP-tested; new visual cashflow delta pending,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,"[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. | [Saldo consolidado21/10/2026] Concluído no recorte. A/B, logout/novo login, recuperação e recusa pela aplicação demonstrados anteriormente. A tesouraria tem prova API de nova sessão e prova visual de recuperação por recarga. Não atribuir ao navegador nova sessão dos valores de tesouraria, nem nova certificação RLS; limites expressos conservados. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.",,20/10/2026,,,,,,,,,,,,,, +AUD-SIM-INT03,Review isolation recovery across projects and sessions,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing controls and accepted proofs to reconcile; no leak established,Two dossiers and new login recovered in browser; cashflow persistence corrected and HTTP-tested; new visual cashflow delta pending,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P0,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,"[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. | [Saldo consolidado21/10/2026] Concluído no recorte. A/B, logout/novo login, recuperação e recusa pela aplicação demonstrados anteriormente. A tesouraria tem prova API de nova sessão e prova visual de recuperação por recarga. Não atribuir ao navegador nova sessão dos valores de tesouraria, nem nova certificação RLS; limites expressos conservados. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Delta QA 23/10/2026 sobre c3f7c3ad] QA: gzip/ETag; idioma guardado e recuperado; versao antiga continua recusada. Ver docs/investmoz/unification/traceability-and-manual.md. Homologacao permanece 7c1f5321; cobertura transversal parcial.",,20/10/2026,,,,,,,,,,,,,, AUD-SIM-INT04,Review stale and historical results and correct dossier,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,"Existing versions/fingerprints; targeted verification pending | 22/10/2026: primeiro incremento QA na branch codex/project-context-continuity-QA; contexto único por dossier/conta, herança com edições preservadas, moeda/IVA mensal e análise territorial explícita. Ver traceability-and-manual.md, secção Continuidade e adaptação contextual. 4 HTTP/74 asserções; 8 frontend; percurso visual A/B e nova sessão. Não encerra adaptação jurídica integral.",Historical results visible after fix; 12 and 24 recovered; hashes unchanged; synthetic temporal resolver versions verified in memory only,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,"22/10/2026: primeiro incremento QA na branch codex/project-context-continuity-QA; contexto único por dossier/conta, herança com edições preservadas, moeda/IVA mensal e análise territorial explícita. Ver traceability-and-manual.md, secção Continuidade e adaptação contextual. 4 HTTP/74 asserções; 8 frontend; percurso visual A/B e nova sessão. Não encerra adaptação jurídica integral.",,,,,diagnosis and targeted corrections,,"[Saldo consolidado21/10/2026] Concluído no recorte demonstrado. Histórico24/12 visível, hashes preservados e versões temporais sintéticas do resolvedor conferidas. Execução fiscal integrada com parâmetros reais pertence ao saldo de02 e não foi demonstrada por estes testes. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | 22/10/2026: primeiro incremento QA na branch codex/project-context-continuity-QA; contexto único por dossier/conta, herança com edições preservadas, moeda/IVA mensal e análise territorial explícita. Ver traceability-and-manual.md, secção Continuidade e adaptação contextual. 4 HTTP/74 asserções; 8 frontend; percurso visual A/B e nova sessão. Não encerra adaptação jurídica integral.",,20/10/2026,,,,,,,,,,Catálogo visual sintético QA; ZEE/ZFI apenas controlos HTTP revertidos; condições/benefícios não executáveis indeterminados; sem ensaio visual integral dos outros consumidores.,,,,Incremento contextual QA para revisão; sem integração main/activação fiscal. AUD-SIM-INT05,Independent financial controls unknown zero units and currencies,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Reconciliation pending; no invented tax assumptions,Independent business-plan and cashflow ledgers passed; complete credit recipe included in screen and exports,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,"[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. | [Saldo consolidado21/10/2026] Concluído nos controlos delimitados. Controlos independentes de caixa/plano/crédito, zero/desconhecido e moedas preservados. Não são validação de taxas legais nem certificação de toda a contabilidade. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.",,20/10/2026,,,,,,,,,,,,,, -AUD-SIM-INT06,Generated reports screen download and pagination,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing exporters; examples for this review still pending,Normal UI CSV and DOCX match shared values; all six DOCX pages checked; native Edge PDF awaits user save; new cashflow visual output pending,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,"[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. | [Exportação nativa 21/10/2026] Saída actual aberta pelo botão do portal: revisão 4, 12 meses, MZN, saldo inicial123/receita900/custos600, calendário10/vazio/0, caixa conhecida3713 e total indeterminado coincidem com o ecrã. Crédito estimado500 identificado como pressuposto, não somado à caixa. Prova portal-print-current-20261021.txt. Relatório de20/10/revisão2 preservado, sem o confundir com o actual. Downloads: sem novo CSV na pasta habitual ou nos temporários locais conferidos; isso não demonstra falha nem cancelamento. Histórico de downloads bloqueado pela política da ferramenta; caminho do ficheiro solicitado antes de repetir. PDF nativo aberto, mas paginação/legibilidade aguardam o ficheiro efectivamente guardado pelo Edge; conversão DOCX/PDF anterior não conta como essa prova. Perda da ligação e bloqueio de controlo durante impressão são limitações da ferramenta, sem falha do simulador demonstrada. | [Fecho da verificação dos ficheiros 21/10/2026] Transferências Windows redireccionada para \\Mac\Home\Downloads; CSV concluído08:12:46, PDF nativo08:17:19. Sem repetir downloads. Os12 registos mensais do CSV coincidem exactamente com o snapshot do ecrã após correspondência de colunas; PDF revisão4 conserva valores mensais, MZN,12meses,123/900/600, crédito estimado500 não caixa e caixa conhecida3713. Os10 PNG do PDF nativo foram inspeccionados: defeito confirmado na linha repetida de rodapé, sobreposta ao corpo perto do topo de páginas, incluindo2,6e10. Conteúdo/valores conferidos; paginação/legibilidade não aceites até correcção e nova prova nativa. Não é conversão DOCX/PDF. Perda de ligação continua limitação da ferramenta e não causou falha do download; sobreposição é defeito distinto demonstrado no PDF. Provas: download-native-inspection-20261021.json, portal-cashflow-20261021.csv, portal-native-20261021.pdf e native-pdf-render/. A indicação anterior de ficheiros pendentes fica superada por esta prova. Sem alteração funcional nem escrita remota. | [Correcção do rodapé nativo QA 21/10/2026] Origem: pedido expresso da Tempo nesta conversa; solução de implementação, sem regra organizacional nova. Causa em createProjectDossierPrintHtml (project-dossier-document.ts): footer position:fixed/bottom:-14mm sobrepunha a área fragmentada de impressão. Substituído na impressão por caixas @page @bottom-left/@bottom-right, dentro da margem inferior reservada20mm, com identificação/revisão e número de página/total. Rodapé normal de ecrã conservado; sem diminuir letras do corpo (10,5pt), tabelas, cabeçalhos ou origens (9pt), nem retirar conteúdo. Mesmo dossier B/revisão4 regenerado pelo botão do portal QA18576 e guardado no Edge, não por conversão DOCX. 10→10 páginas A4; comparação automática do corpo por página, descontando apenas hora de emissão/rodapé/contador, coincide integralmente. Inspecção visual dos10 PNG: sem sobreposição, cabeçalhos de tabela repetidos, quebras/linhas preservadas, última página íntegra e numeração1/10→10/10. Rodapés medidos inteiramente na margem inferior. Ecrã QA conferido após build; estilos screen e geradores DOCX/ZIP inalterados por comparação de fontes. 10 testes do exportador, TypeScript e build passaram; escape da revisão na nova string CSS verificado. Reutilizadas provas de valores e CSV aceites; não repetido download/aritmética. Provas: native-footer-fix-20261021.json; portal-native-fixed-20261021.pdf; native-footer-before-after-20261021.jpg; native-pdf-fixed-render/; screen-after-print-fix-20261021.png. Fechado exclusivamente o defeito de rodapé neste relatório QA; limitações e restantes critérios de AUD-SIM-INT06 mantêm âmbito próprio. Relato anterior de sobreposição fica superado neste delta pela comparação antes/depois. Branch codex/simulator-integrated-review separada de main, sem integração, escrita remota ou disponibilização activa; revisão prevista mantém-se pendente. | [Saldo consolidado21/10/2026] Concluído na apresentação e comparação deste relatório. Rodapé do PDF nativo fechado em QA segundo provas apresentadas;10 páginas/valores/CSV não repetidos. Tempo declarou que não abriu imagem/PDF pelos caminhos locais: registar aceitação das provas, não revisão visual directa do utilizador. Outras provas de relatório conservadas; falhas interactivas de exportação/anexos mantêm saldo em07. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação.",,20/10/2026,,,,,,,,,,,,,, -AUD-SIM-INT07,Explicit validate calculate save export errors and retry,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing idempotency; consumer-level verification pending,Calculation navigation and failed-save retry fixed and browser-verified; normal downloads verified; interactive attachment blocked by extension permission,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,," | [Saldo consolidado21/10/2026] Parcial. Gravação503→nova tentativa→recuperação e exportações normais demonstradas. Faltam falhas dirigidas de geração Word/ZIP/PDF e retoma; teste interactivo de limite de anexo não ocorreu por bloqueio da ferramenta antes do envio. Nova leitura do código: três catch apresentam cause.message para Error, podendo mostrar texto técnico sem tradução. Sem nova falha de execução alegada. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Candidata21/10/2026] Concluído o recorte dirigido em QA: Word/PDF/ZIP falha única injectada→mensagem portuguesa→nova tentativa no mesmo botão. ZIP preserva título editado, secção desmarcada e anexo; arquivo real contém uma só cópia original/hash igual. Recusa16MB observada mantendo1anexo; nova selecção válida limpa erro e mantém1entrada. Guarda síncrona impede concorrência/duplicação; teste de falha de leitura preserva lista/legenda e permite retoma.4testes dirigidos passaram, TypeScript/build passaram. UI usa componente/geradores reais num invólucro QA sintético; a injecção e a omissão da impressão automática nesse ensaio não integram a entrega. Não repetidas10páginas/valores/CSV aceites. | [Limite conferido21/10/2026] c22003a3 ja definia15*1024*1024 bytes por anexo,10anexos/50MiB total;13f68f1b nao reduz esses limites. Teste16MiB=16777216bytes excede15MiB=15728640bytes. Limite2MiB de incorporacao textual distinto de aceitar original no ZIP. Origem: escolha tecnica preexistente, sem inferir aprovacao organizacional especifica. Entrega: melhorias de planeamento financeiro, recuperacao, exportacao e idioma; revisao antes de main.",,20/10/2026,,,,,,,,,,,,,, +AUD-SIM-INT06,Generated reports screen download and pagination,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing exporters; examples for this review still pending,Normal UI CSV and DOCX match shared values; all six DOCX pages checked; native Edge PDF awaits user save; new cashflow visual output pending,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,,"[Retoma 21/10/2026] QA 18576, clone simulator_review_delta_20261020_test: sessão sintética renovada por login normal; gravação autenticada e recarga recuperaram saldo 123, receita 900, custos 600, calendário mensal 10/vazio/0, crédito IVA estimado 500. Caixa conhecida 3713 MZN; total indeterminado desde mês 2; crédito estimado excluído da caixa. Prova visual cashflow-recovered-20261021.png e snapshot .txt em outputs/simulator-continuation-20261020. Não certifica recuperação destes novos valores após outro login, CSV/DOCX ou PDF nativo. Ligação de controlo do navegador perdida durante espera de download; não atribuir falha à aplicação. Leitura remota aguarda identificação da ligação SSH; zero alterações no destino. | [Exportação nativa 21/10/2026] Saída actual aberta pelo botão do portal: revisão 4, 12 meses, MZN, saldo inicial123/receita900/custos600, calendário10/vazio/0, caixa conhecida3713 e total indeterminado coincidem com o ecrã. Crédito estimado500 identificado como pressuposto, não somado à caixa. Prova portal-print-current-20261021.txt. Relatório de20/10/revisão2 preservado, sem o confundir com o actual. Downloads: sem novo CSV na pasta habitual ou nos temporários locais conferidos; isso não demonstra falha nem cancelamento. Histórico de downloads bloqueado pela política da ferramenta; caminho do ficheiro solicitado antes de repetir. PDF nativo aberto, mas paginação/legibilidade aguardam o ficheiro efectivamente guardado pelo Edge; conversão DOCX/PDF anterior não conta como essa prova. Perda da ligação e bloqueio de controlo durante impressão são limitações da ferramenta, sem falha do simulador demonstrada. | [Fecho da verificação dos ficheiros 21/10/2026] Transferências Windows redireccionada para \\Mac\Home\Downloads; CSV concluído08:12:46, PDF nativo08:17:19. Sem repetir downloads. Os12 registos mensais do CSV coincidem exactamente com o snapshot do ecrã após correspondência de colunas; PDF revisão4 conserva valores mensais, MZN,12meses,123/900/600, crédito estimado500 não caixa e caixa conhecida3713. Os10 PNG do PDF nativo foram inspeccionados: defeito confirmado na linha repetida de rodapé, sobreposta ao corpo perto do topo de páginas, incluindo2,6e10. Conteúdo/valores conferidos; paginação/legibilidade não aceites até correcção e nova prova nativa. Não é conversão DOCX/PDF. Perda de ligação continua limitação da ferramenta e não causou falha do download; sobreposição é defeito distinto demonstrado no PDF. Provas: download-native-inspection-20261021.json, portal-cashflow-20261021.csv, portal-native-20261021.pdf e native-pdf-render/. A indicação anterior de ficheiros pendentes fica superada por esta prova. Sem alteração funcional nem escrita remota. | [Correcção do rodapé nativo QA 21/10/2026] Origem: pedido expresso da Tempo nesta conversa; solução de implementação, sem regra organizacional nova. Causa em createProjectDossierPrintHtml (project-dossier-document.ts): footer position:fixed/bottom:-14mm sobrepunha a área fragmentada de impressão. Substituído na impressão por caixas @page @bottom-left/@bottom-right, dentro da margem inferior reservada20mm, com identificação/revisão e número de página/total. Rodapé normal de ecrã conservado; sem diminuir letras do corpo (10,5pt), tabelas, cabeçalhos ou origens (9pt), nem retirar conteúdo. Mesmo dossier B/revisão4 regenerado pelo botão do portal QA18576 e guardado no Edge, não por conversão DOCX. 10→10 páginas A4; comparação automática do corpo por página, descontando apenas hora de emissão/rodapé/contador, coincide integralmente. Inspecção visual dos10 PNG: sem sobreposição, cabeçalhos de tabela repetidos, quebras/linhas preservadas, última página íntegra e numeração1/10→10/10. Rodapés medidos inteiramente na margem inferior. Ecrã QA conferido após build; estilos screen e geradores DOCX/ZIP inalterados por comparação de fontes. 10 testes do exportador, TypeScript e build passaram; escape da revisão na nova string CSS verificado. Reutilizadas provas de valores e CSV aceites; não repetido download/aritmética. Provas: native-footer-fix-20261021.json; portal-native-fixed-20261021.pdf; native-footer-before-after-20261021.jpg; native-pdf-fixed-render/; screen-after-print-fix-20261021.png. Fechado exclusivamente o defeito de rodapé neste relatório QA; limitações e restantes critérios de AUD-SIM-INT06 mantêm âmbito próprio. Relato anterior de sobreposição fica superado neste delta pela comparação antes/depois. Branch codex/simulator-integrated-review separada de main, sem integração, escrita remota ou disponibilização activa; revisão prevista mantém-se pendente. | [Saldo consolidado21/10/2026] Concluído na apresentação e comparação deste relatório. Rodapé do PDF nativo fechado em QA segundo provas apresentadas;10 páginas/valores/CSV não repetidos. Tempo declarou que não abriu imagem/PDF pelos caminhos locais: registar aceitação das provas, não revisão visual directa do utilizador. Outras provas de relatório conservadas; falhas interactivas de exportação/anexos mantêm saldo em07. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Delta QA 23/10/2026 sobre c3f7c3ad] QA: origem por campo em snapshot/relatorio/CSV e edicao posterior distinguida. Ver docs/investmoz/unification/traceability-and-manual.md. Homologacao permanece 7c1f5321; cobertura transversal parcial.",,20/10/2026,,,,,,,,,,,,,, +AUD-SIM-INT07,Explicit validate calculate save export errors and retry,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,Existing idempotency; consumer-level verification pending,Calculation navigation and failed-save retry fixed and browser-verified; normal downloads verified; interactive attachment blocked by extension permission,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,," | [Saldo consolidado21/10/2026] Parcial. Gravação503→nova tentativa→recuperação e exportações normais demonstradas. Faltam falhas dirigidas de geração Word/ZIP/PDF e retoma; teste interactivo de limite de anexo não ocorreu por bloqueio da ferramenta antes do envio. Nova leitura do código: três catch apresentam cause.message para Error, podendo mostrar texto técnico sem tradução. Sem nova falha de execução alegada. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Candidata21/10/2026] Concluído o recorte dirigido em QA: Word/PDF/ZIP falha única injectada→mensagem portuguesa→nova tentativa no mesmo botão. ZIP preserva título editado, secção desmarcada e anexo; arquivo real contém uma só cópia original/hash igual. Recusa16MB observada mantendo1anexo; nova selecção válida limpa erro e mantém1entrada. Guarda síncrona impede concorrência/duplicação; teste de falha de leitura preserva lista/legenda e permite retoma.4testes dirigidos passaram, TypeScript/build passaram. UI usa componente/geradores reais num invólucro QA sintético; a injecção e a omissão da impressão automática nesse ensaio não integram a entrega. Não repetidas10páginas/valores/CSV aceites. | [Limite conferido21/10/2026] c22003a3 ja definia15*1024*1024 bytes por anexo,10anexos/50MiB total;13f68f1b nao reduz esses limites. Teste16MiB=16777216bytes excede15MiB=15728640bytes. Limite2MiB de incorporacao textual distinto de aceitar original no ZIP. Origem: escolha tecnica preexistente, sem inferir aprovacao organizacional especifica. Entrega: melhorias de planeamento financeiro, recuperacao, exportacao e idioma; revisao antes de main. | [Delta QA 23/10/2026 sobre c3f7c3ad] QA: ETag fraco normalizado no cliente sem retry forcado ou bypass de concorrencia. Ver docs/investmoz/unification/traceability-and-manual.md. Homologacao permanece 7c1f5321; cobertura transversal parcial.",,20/10/2026,,,,,,,,,,,,,, AUD-SIM-INT08,Portuguese across portal and simulator outputs,Tempo request 20/10/2026; integrated simulator review; same manual,simulator and shared language,,,QA only,"Partial QA fix: rate labels, password confirmation/security and spinner; wider inventory pending",Generic HTTP and fiscal-expression validation messages translated; status/correlation verified; selectable translations and source titles preserved,QA continuation verified; precise open inputs and cashflow visual check in same manual; 2-4 h remaining,,,P1,,,,,,,,,,,,,,,,,,diagnosis and targeted corrections,," | [Saldo consolidado21/10/2026] Parcial. Inventário estático,404 real e traduções de validação preservados. Classificar/apresentar em português os erros dinâmicos das bibliotecas de exportação (cause.message, três consumidores) e conferir estados residuais/governação delimitados; não certificar todos os textos dinâmicos do catálogo. Ver saldo dos oito itens no manual existente; sem fecho global por esta reconciliação. | [Candidata21/10/2026] Mensagens dinâmicas dos exportadores Word/ZIP/PDF/CSV e leitura de anexos usam texto português por operação, preservação e nova tentativa com referênciaEXP. Excepção original só na consola técnica local com referência/operação; sem telemetria nem auditoria remota novas. UI confirmou português nas3falhas de geração;4testes verificam separação mensagem/diagnóstico e correlação em5operações. Traduções/404/validações anteriores reutilizados. Fecho dos consumidores afectados, não certificação de todos os textos de catálogo ou nova sessão administrativa de governação.",,20/10/2026,,,,,,,,,,,,,, diff --git a/resources/js/components/simulation-form.tsx b/resources/js/components/simulation-form.tsx index bd218e00..674f12ce 100644 --- a/resources/js/components/simulation-form.tsx +++ b/resources/js/components/simulation-form.tsx @@ -1,3 +1,4 @@ +import { simulationDraftEtag } from '@/lib/simulation-draft-etag'; import { SimulationPublishedIva, type PublishedIvaInput } from './simulation-published-iva'; import { SimulationMonthlyIva } from './simulation-monthly-iva'; import { inheritSimulatorFields, reusedInvestmentValue } from '@/lib/context-inheritance'; @@ -1248,7 +1249,7 @@ export function SimulationForm() { if (payload.data) { if (cancelled) return; draftIdRef.current = payload.data.id; - draftEtagRef.current = response.headers.get('ETag'); + draftEtagRef.current = simulationDraftEtag(response.headers.get('ETag')); setDraftVersionNo(payload.data.version_no); setDraftExpiresAt(payload.data.expires_at ?? null); if (canSavePersonalData) @@ -1370,7 +1371,7 @@ export function SimulationForm() { if (currentDraft) { draftIdRef.current = currentDraft.id; draftEtagRef.current = - currentResponse.headers.get('ETag'); + simulationDraftEtag(currentResponse.headers.get('ETag')); sessionStorage.setItem(draftIdKey, currentDraft.id); } } @@ -1432,7 +1433,7 @@ export function SimulationForm() { ); draftIdRef.current = payload.data.id; setServerFieldErrors({}); - draftEtagRef.current = response.headers.get('ETag'); + draftEtagRef.current = simulationDraftEtag(response.headers.get('ETag')); setDraftVersionNo(payload.data.version_no); lastSavedFingerprintRef.current = fingerprint; sessionStorage.setItem(draftIdKey, payload.data.id); @@ -1593,7 +1594,7 @@ export function SimulationForm() { ) throw new Error('Não foi possível abrir este dossier privado.'); draftIdRef.current = payload.data.id; - draftEtagRef.current = response.headers.get('ETag'); + draftEtagRef.current = simulationDraftEtag(response.headers.get('ETag')); setDraftVersionNo(payload.data.version_no); setDraftExpiresAt(null); setSelectedAccountDraftId(payload.data.id); @@ -1656,7 +1657,7 @@ export function SimulationForm() { ); const record = (payload as { data: ApiSimulationDraftV1 }).data; draftIdRef.current = record.id; - draftEtagRef.current = response.headers.get('ETag'); + draftEtagRef.current = simulationDraftEtag(response.headers.get('ETag')); setDraftVersionNo(record.version_no); setDraftExpiresAt(null); setSelectedAccountDraftId(record.id); @@ -1756,7 +1757,7 @@ export function SimulationForm() { (payload as { expires_at: string }).expires_at, ); draftEtagRef.current = - response.headers.get('ETag') ?? draftEtagRef.current; + simulationDraftEtag(response.headers.get('ETag')) ?? draftEtagRef.current; } setContinueLaterSaved(true); setSaveStatus( @@ -2375,7 +2376,7 @@ export function SimulationForm() { ); const draftId = draftIdValue; draftIdRef.current = draftId; - draftEtagRef.current = currentResponse.headers.get('ETag'); + draftEtagRef.current = simulationDraftEtag(currentResponse.headers.get('ETag')); if (!draftEtagRef.current) throw new Error( 'O servidor não devolveu a versão actual do rascunho. Recarregue e tente novamente.', @@ -2409,7 +2410,7 @@ export function SimulationForm() { throw new Error( 'O servidor devolveu uma confirmação de anexação inválida.', ); - draftEtagRef.current = response.headers.get('ETag'); + draftEtagRef.current = simulationDraftEtag(response.headers.get('ETag')); const sources = payload.data?.simulation_sources; if (Array.isArray(sources)) setSimulationSources(sources); setDraft((currentDraft) => ({ diff --git a/resources/js/lib/context-inheritance.ts b/resources/js/lib/context-inheritance.ts index 165b0736..85c3f253 100644 --- a/resources/js/lib/context-inheritance.ts +++ b/resources/js/lib/context-inheritance.ts @@ -72,6 +72,26 @@ export function reusedInvestmentValue(scope: string, key: string, values: Record } catch { return null; } } +/** Report the lineage without changing a stored answer or disclosing the tracking hash. */ +export function investmentFieldOrigin(values: Record, key: string, fallback: string): string { + try { + const reuse = JSON.parse(String(values.context_reuse ?? '')); + if (reuse.version !== 1 || !/^dossier:.+:simulator$/.test(reuse.scope) || !reuse.inherited?.[key]) return fallback; + const snapshot = JSON.parse(String(values.context_applicability_snapshot ?? '')); + const origin = snapshot.field_provenance?.[key]; + const source = origin?.kind === 'opportunity' ? `oportunidade «${origin.title ?? 'referência guardada'}»` + : origin?.kind === 'process_declaration' || origin?.kind === 'dossier' ? 'dados introduzidos pelo utilizador no dossier' + : origin?.kind === 'sector' ? `catálogo sectorial «${origin.title ?? 'referência guardada'}»` + : origin?.kind === 'territory' ? `catálogo territorial «${origin.title ?? 'referência guardada'}»` + : origin?.kind === 'linked_simulation' ? 'simulador ligado ao mesmo dossier' + : 'dossier de investimento'; + const version = origin?.version ? `, versão ${origin.version}` : ''; + return reuse.inherited[key] === signature(values[key]) + ? `Herdado de ${source}${version}` + : `Alterado posteriormente pelo utilizador; origem anterior: ${source}${version}`; + } catch { return fallback; } +} + export function inheritSimulatorFields>(draft:T): T { const source:Record={}; const plan=draft.business_plan_snapshot ? restoreBusinessPlanSnapshot(String(draft.business_plan_snapshot)) : null; diff --git a/resources/js/lib/project-dossier-report.ts b/resources/js/lib/project-dossier-report.ts index c351f63e..11f18428 100644 --- a/resources/js/lib/project-dossier-report.ts +++ b/resources/js/lib/project-dossier-report.ts @@ -229,8 +229,8 @@ function makeSections( { field: 'Projecto', value: absent(text(draft, 'project_title')), - origin: 'Informação introduzida no dossier', - classification: 'Declarada pelo utilizador', + origin: rows.find(row => row[1] === 'Título')?.[3] ?? 'Informação introduzida no dossier', + classification: 'Origem indicada no dossier', }, { field: 'Data de geração', diff --git a/resources/js/lib/project-dossier.ts b/resources/js/lib/project-dossier.ts index 79d08979..b5261920 100644 --- a/resources/js/lib/project-dossier.ts +++ b/resources/js/lib/project-dossier.ts @@ -1,4 +1,5 @@ import { displayValue } from './display-value.ts'; +import { investmentFieldOrigin } from './context-inheritance.ts'; import { investmentLandPreparation } from './investment-land-preparation.ts'; import { monthlyIvaInput, monthlyIvaKey, readMonthlyIvaRecord, readMonthlyIvaSettings } from './monthly-iva.ts'; import { modeloAPurchaseReferences } from './iva-modelo-a.ts'; @@ -705,7 +706,11 @@ export function buildProjectDossierRows( if (land.need === 'yes') rows.push(['Localização', 'Situação do terreno', String(draft.land_status_label ?? ({ owned: 'Próprio', duat: 'DUAT', leased: 'Arrendado', requested: 'Pedido em curso', unknown: 'Por determinar' } as Record)[String(draft.land_status)] ?? 'Informação em falta'), 'Informação declarada; não comprova título ou disponibilidade']); else rows.push(['Localização', 'Preparação do terreno', land.need === 'no' ? 'Situação do terreno não aplicável à preparação actual; valores anteriores conservados.' : 'Definir se precisa de terra; pode continuar a preparar o investimento.', 'Sem inferência de dispensa legal']); } - return rows; + const lineageFields: Record = {'Título':'project_title','Resumo':'project_summary','Sector':'sector_id','Província':'province_id','Distrito':'district_id','Posto administrativo':'admin_post_id','Localidade':'locality_id'}; + return rows.map(row => { + const key = lineageFields[row[1]]; + return key && row[2] ? [row[0], row[1], row[2], investmentFieldOrigin(draft, key, row[3])] : row; + }); } export function exportProjectDossierCsv( diff --git a/resources/js/lib/simulation-draft-etag.ts b/resources/js/lib/simulation-draft-etag.ts new file mode 100644 index 00000000..dc7f0aaf --- /dev/null +++ b/resources/js/lib/simulation-draft-etag.ts @@ -0,0 +1,5 @@ +/** Compression can mark the representation weak; the draft version token itself is unchanged. */ +export function simulationDraftEtag(header: string | null): string | null { + if (header && /^W\/"sfd-[0-9a-f-]{36}-v[1-9][0-9]*"$/i.test(header)) return header.slice(2); + return header; +} diff --git a/tests/Feature/InvestmentPortfolioContinuityTest.php b/tests/Feature/InvestmentPortfolioContinuityTest.php index 358f871a..6a7864c9 100644 --- a/tests/Feature/InvestmentPortfolioContinuityTest.php +++ b/tests/Feature/InvestmentPortfolioContinuityTest.php @@ -17,6 +17,41 @@ use Tests\TestCase; final class InvestmentPortfolioContinuityTest extends TestCase { use DatabaseTransactions; + + public function test_draft_version_guard_still_rejects_a_real_stale_version(): void + { + $draft = new \App\Models\SimulationFormDraft; + $draft->forceFill(['id'=>(string)Str::uuid(),'version_no'=>2]); + $service=app(\App\Services\SimulationFormDrafts::class); + $guard=new \ReflectionMethod($service,'requireMatch'); + $tag=$service->etag($draft); + foreach (['W/'.$tag, str_replace('-v2','-v1',$tag)] as $invalid) { + $request=\Illuminate\Http\Request::create('/','PUT'); $request->headers->set('If-Match',$invalid); + try { $guard->invoke($service,$request,$draft); $this->fail('A mismatching version must be rejected'); } + catch (\Symfony\Component\HttpKernel\Exception\HttpException $error) { $this->assertSame(412,$error->getStatusCode()); } + } + $request=\Illuminate\Http\Request::create('/','PUT'); $request->headers->set('If-Match',$tag); + $this->assertNull($guard->invoke($service,$request,$draft)); + } + + public function test_beira_documentary_membership_opens_the_same_published_opportunities_without_inferring_by_province(): void + { + $portfolio = InvestmentPortfolioController::visible()->where('content->code', 'COR-02')->firstOrFail(); + $service = app(CorridorAgribusinessOpportunities::class); + $ids = $service->portfolioOpportunityIds($portfolio); + $this->assertCount(7, $ids); + $this->get('/carteira/'.$portfolio->id)->assertOk()->assertInertia(fn(Assert $page) => $page->has('opportunities', 7)); + $catalogueIds=app(\App\Services\InvestmentCatalogue::class)->query(['corridor_id'=>$portfolio->corridor_id])->pluck('opportunities.id')->all(); + $this->assertSame([], array_values(array_diff($ids,$catalogueIds))); + $opportunity=Opportunity::findOrFail($ids[0]); + $this->get('/oportunidades/'.$opportunity->slug.'?from_dossier='.$portfolio->id)->assertOk(); + $origins=app(InvestmentOpportunityChoices::class)->portfolioOrigins([$portfolio->id],$opportunity->id); + $this->assertSame($portfolio->id,$origins[0]['id']); + $this->assertSame('corridor',$origins[0]['kind']); + $other=Opportunity::published()->whereNotIn('id',$ids)->firstOrFail(); + $this->get('/oportunidades/'.$other->slug.'?from_dossier='.$portfolio->id)->assertNotFound(); + $this->assertSame([], $service->portfolioOpportunityIds(InvestmentPortfolioController::visible()->whereNotNull('project_id')->firstOrFail())); + } private User $actor; protected function setUp(): void { diff --git a/tests/frontend/investment-field-origin.test.mjs b/tests/frontend/investment-field-origin.test.mjs new file mode 100644 index 00000000..588dbe19 --- /dev/null +++ b/tests/frontend/investment-field-origin.test.mjs @@ -0,0 +1,22 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { inheritCompatibleFields } from '../../resources/js/lib/context-inheritance.ts'; +import { buildProjectDossierRows, exportProjectDossierCsv } from '../../resources/js/lib/project-dossier.ts'; +import { createProjectDossierReport, createProjectDossierPrintHtml } from '../../resources/js/lib/project-dossier-report.ts'; + +test('reports distinguish opportunity inheritance, dossier declarations and later edits without rewriting the snapshot', async()=>{ + const source={project_title:'Título do promotor',project_summary:'Resumo da oportunidade',sector_id:'1',sector_id_label:'Agronegócio',context_applicability_snapshot:JSON.stringify({version:1,field_provenance:{project_summary:{kind:'opportunity',title:'Florestas — Corredor da Beira',version:2},sector_id:{kind:'opportunity',title:'Florestas — Corredor da Beira',version:2},project_title:{kind:'process_declaration',context_revision:1}}})}; + const inherited=inheritCompatibleFields('dossier:A:simulator',{},source,null); + const draft={...inherited.patch,context_reuse:inherited.metadata}; + let rows=buildProjectDossierRows(draft); + assert.match(rows.find(r=>r[1]==='Resumo')[3],/Herdado de oportunidade «Florestas/); + assert.match(rows.find(r=>r[1]==='Título')[3],/dados introduzidos pelo utilizador no dossier/); + const edited={...draft,project_summary:'Resumo alterado no simulador'}; const before=JSON.stringify(edited); + rows=buildProjectDossierRows(edited); + assert.match(rows.find(r=>r[1]==='Resumo')[3],/Alterado posteriormente pelo utilizador; origem anterior: oportunidade/); + assert.match(exportProjectDossierCsv(edited),/origem anterior: oportunidade/); + const report=createProjectDossierReport(edited,{selectedSections:new Set(['summary','project'])}); + assert.match(await createProjectDossierPrintHtml(report,[],false),/Resumo alterado no simulador/); + assert.equal(JSON.stringify(edited),before); + assert.equal(buildProjectDossierRows({project_summary:'Legado'}).find(r=>r[1]==='Resumo')[3],'Declarado pelo utilizador'); +}); diff --git a/tests/frontend/simulation-draft-etag.test.mjs b/tests/frontend/simulation-draft-etag.test.mjs new file mode 100644 index 00000000..1d54ac74 --- /dev/null +++ b/tests/frontend/simulation-draft-etag.test.mjs @@ -0,0 +1,12 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { simulationDraftEtag } from '../../resources/js/lib/simulation-draft-etag.ts'; + +test('compressed draft reads use the exact version token while genuinely stale versions stay different', () => { + const tag='"sfd-6c07a968-7ff9-42fa-b252-072bb1acf221-v2"'; + assert.equal(simulationDraftEtag(`W/${tag}`),tag); + assert.equal(simulationDraftEtag(tag),tag); + assert.notEqual(simulationDraftEtag(`W/${tag}`),tag.replace('-v2','-v3')); + assert.equal(simulationDraftEtag('W/"another-resource"'),'W/"another-resource"'); + assert.equal(simulationDraftEtag(null),null); +});