Preserve S08 hash lifecycle while revealing linked SWOT tasks
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-11-10 18:17:13 +02:00
parent c790159250
commit 1c75a30a60
5 changed files with 104 additions and 10 deletions
@@ -1,3 +1,7 @@
## 10/11/2026 — correcção dirigida das âncoras antes da promoção
O executor recusou c7901592 por um caso frontendS08 (1falha/1788), mantendo7476dc34 activo. Diagnóstico delimitado: a página tinha dois efeitos de âncora e o teste existente conservava uma única sequência de dependências. A abertura SWOT foi integrada no listener de hash já existente, também para mudanças posteriores de #tarefas, sem alterar a guarda/foco/expansão de S08. O teste S08 aceite conserva as suas asserções; acrescentado caso que alterna #tarefas/#S08, verifica isolamento no dossier e limpeza do listener.15casos dirigidos APIEX/SWOT/âncoras aprovados. Nenhuma alteração de dados, permissões, fontes normativas ou configuração; entrega autorizada retomada no mesmo mecanismo, sem mascarar a falha nem desactivar verificações. Recibos finais continuam a determinar a revisão instalada.
## 10/11/2026 — Concursos visíveis no menu principal; instalação agrupada autorizada
Conferência delimitada do destino7476dc34 por SSH autenticado: `procurement.enabled=true`, módulo disponível, tabelas presentes; `/concursos` e raiz HTTP200 com `procurementEnabled=true`. Preservados um concurso em rascunho e zero versões publicadas. O módulo não desapareceu nem estava desligado: a ligação estava dentro de Descoberta. Pedido Tempo «onde foram parar concurso? depois de resolver isso autorizo a instalação»: entrada promovida a grupo próprio **Concursos e anúncios** no menu horizontal comum, com **Consultar concursos e anúncios** e, após autenticação, **Os concursos que acompanho**. Retirada só a ligação duplicada de Descoberta; cinco atalhos principais e autorizações editoriais preservados. Não cria/publica concursos nem muda flags, dados ou schema.
@@ -1,3 +1,7 @@
## 10/11/2026 — correcção dirigida das âncoras antes da promoção
O executor recusou c7901592 por um caso frontendS08 (1falha/1788), mantendo7476dc34 activo. Diagnóstico delimitado: a página tinha dois efeitos de âncora e o teste existente conservava uma única sequência de dependências. A abertura SWOT foi integrada no listener de hash já existente, também para mudanças posteriores de #tarefas, sem alterar a guarda/foco/expansão de S08. O teste S08 aceite conserva as suas asserções; acrescentado caso que alterna #tarefas/#S08, verifica isolamento no dossier e limpeza do listener.15casos dirigidos APIEX/SWOT/âncoras aprovados. Nenhuma alteração de dados, permissões, fontes normativas ou configuração; entrega autorizada retomada no mesmo mecanismo, sem mascarar a falha nem desactivar verificações. Recibos finais continuam a determinar a revisão instalada.
## 10/11/2026 — Concursos visíveis no menu principal; instalação agrupada autorizada
Conferência delimitada do destino7476dc34 por SSH autenticado: `procurement.enabled=true`, módulo disponível, tabelas presentes; `/concursos` e raiz HTTP200 com `procurementEnabled=true`. Preservados um concurso em rascunho e zero versões publicadas. O módulo não desapareceu nem estava desligado: a ligação estava dentro de Descoberta. Pedido Tempo «onde foram parar concurso? depois de resolver isso autorizo a instalação»: entrada promovida a grupo próprio **Concursos e anúncios** no menu horizontal comum, com **Consultar concursos e anúncios** e, após autenticação, **Os concursos que acompanho**. Retirada só a ligação duplicada de Descoberta; cinco atalhos principais e autorizações editoriais preservados. Não cria/publica concursos nem muda flags, dados ou schema.
@@ -1,3 +1,7 @@
## 10/11/2026 — correcção dirigida das âncoras antes da promoção
O executor recusou c7901592 por um caso frontendS08 (1falha/1788), mantendo7476dc34 activo. Diagnóstico delimitado: a página tinha dois efeitos de âncora e o teste existente conservava uma única sequência de dependências. A abertura SWOT foi integrada no listener de hash já existente, também para mudanças posteriores de #tarefas, sem alterar a guarda/foco/expansão de S08. O teste S08 aceite conserva as suas asserções; acrescentado caso que alterna #tarefas/#S08, verifica isolamento no dossier e limpeza do listener.15casos dirigidos APIEX/SWOT/âncoras aprovados. Nenhuma alteração de dados, permissões, fontes normativas ou configuração; entrega autorizada retomada no mesmo mecanismo, sem mascarar a falha nem desactivar verificações. Recibos finais continuam a determinar a revisão instalada.
## 10/11/2026 — Concursos visíveis no menu principal; instalação agrupada autorizada
Conferência delimitada do destino7476dc34 por SSH autenticado: `procurement.enabled=true`, módulo disponível, tabelas presentes; `/concursos` e raiz HTTP200 com `procurementEnabled=true`. Preservados um concurso em rascunho e zero versões publicadas. O módulo não desapareceu nem estava desligado: a ligação estava dentro de Descoberta. Pedido Tempo «onde foram parar concurso? depois de resolver isso autorizo a instalação»: entrada promovida a grupo próprio **Concursos e anúncios** no menu horizontal comum, com **Consultar concursos e anúncios** e, após autenticação, **Os concursos que acompanho**. Retirada só a ligação duplicada de Descoberta; cinco atalhos principais e autorizações editoriais preservados. Não cria/publica concursos nem muda flags, dados ou schema.
+21 -10
View File
@@ -486,25 +486,36 @@ export default function InvestmentPath(path: Path) {
window.location.hash,
stageCodes.split(','),
);
if (
path.preparation_action &&
window.location.hash === '#tarefas'
) {
const target =
root.current.ownerDocument.getElementById('tarefas');
if (
target?.tagName === 'DETAILS' &&
root.current.contains(target)
) {
(target as HTMLDetailsElement).open = true;
target.scrollIntoView({ block: 'start' });
}
}
}
};
reveal();
window.addEventListener('hashchange', reveal);
return () => window.removeEventListener('hashchange', reveal);
}, [path.case.id, path.context_opportunity, stageCodes, path.next?.code]);
}, [
path.case.id,
path.context_opportunity,
stageCodes,
path.next?.code,
path.preparation_action?.id,
]);
const { auth, flash } = usePage<{
auth: { user: { id: number } };
flash: { success?: string };
}>().props;
useEffect(() => {
if (path.preparation_action && window.location.hash === '#tarefas') {
const target = document.getElementById('tarefas');
if (target instanceof HTMLDetailsElement) {
target.open = true;
target.scrollIntoView({ block: 'start' });
}
}
}, [path.preparation_action]);
const facts = useForm({
version: path.case.version,
context_opportunity: path.context_opportunity,
+71
View File
@@ -270,3 +270,74 @@ test("page reveals the initial hash and server next-stage updates, listens for h
globalThis.window = previousWindow;
}
});
test('SWOT task anchor uses the shared hash listener inside the dossier and preserves S08', () => {
const f = targetFixture(),
effects = [],
listeners = new Map();
const previousWindow = globalThis.window;
const task = {
tagName: 'DETAILS',
open: false,
scrollIntoView: (options) => f.calls.push(['task-scroll', options]),
};
f.root.ownerDocument.getElementById = (id) =>
id === 'tarefas' ? task : id === 'S08' ? f.target : null;
f.root.contains = (node) => node === task || node === f.target;
globalThis.window = {
location: { hash: '#tarefas' },
addEventListener: (type, fn) => listeners.set(type, fn),
removeEventListener: (type) => listeners.delete(type),
};
try {
const h = harness(
{
investment_context: null,
facts: {},
fact_reasons: {},
phases: {},
fintech: null,
sector_contexts: [],
requests: [],
tasks: [],
results: [],
tools: {},
content_sources: [],
services: [],
steps: [{ code: 'S08', preparation_allowed: true }],
preparation_action: {
id: 'swot-qa',
title: 'Risco QA',
kind: 'risk',
opportunity: 'opportunity:42',
},
},
{
...React,
useRef: () => ({ current: f.root }),
useEffect: (effect) => effects.push(effect),
useState: (initial) => [
typeof initial === 'function' ? initial() : initial,
() => {},
],
},
);
h.renderPage();
assert.equal(effects.length, 1);
const cleanup = effects[0]();
assert.equal(task.open, true);
assert.equal(f.phase.open, false);
window.location.hash = '#S08';
listeners.get('hashchange')();
assert.equal(f.phase.open, true);
task.open = false;
window.location.hash = '#tarefas';
listeners.get('hashchange')();
assert.equal(task.open, true);
assert.equal(f.calls.filter((c) => c[0] === 'task-scroll').length, 2);
cleanup();
assert.equal(listeners.size, 0);
} finally {
globalThis.window = previousWindow;
}
});