Consume weighted search answers and suppress dead chips

This commit is contained in:
master
2026-03-07 18:38:02 +02:00
parent 86a4928109
commit e295768662
9 changed files with 812 additions and 121 deletions

View File

@@ -54,6 +54,74 @@ const policyBlockerResponse = buildResponse(
},
);
const weightedOverflowResponse = buildResponse(
'critical findings',
[
findingCard({
cveId: 'CVE-2024-21626',
title: 'CVE-2024-21626 in api-gateway',
snippet: 'Reachable critical vulnerability remains the strongest match for this page.',
severity: 'critical',
}),
],
undefined,
{
contextAnswer: {
status: 'grounded',
code: 'retrieved_scope_weighted_evidence',
summary: 'Current-page findings matched first, with one policy blocker held as related overflow.',
reason: 'The search weighted the active findings page first.',
evidence: 'Grounded in 2 sources across Findings and Policy.',
citations: [
{
entityKey: 'cve:CVE-2024-21626',
title: 'CVE-2024-21626 in api-gateway',
domain: 'findings',
},
],
questions: [
{
query: 'What evidence blocks this release?',
kind: 'follow_up',
},
],
},
overflow: {
currentScopeDomain: 'findings',
reason: 'Policy results remain relevant but are weaker than the current findings context.',
cards: [
policyCard({
ruleId: 'POL-118',
title: 'POL-118 release blocker',
snippet: 'Production rollout is blocked while this CVE remains unresolved.',
}),
],
},
coverage: {
currentScopeDomain: 'findings',
currentScopeWeighted: true,
domains: [
{
domain: 'findings',
candidateCount: 3,
visibleCardCount: 1,
topScore: 0.96,
isCurrentScope: true,
hasVisibleResults: true,
},
{
domain: 'policy',
candidateCount: 1,
visibleCardCount: 1,
topScore: 0.74,
isCurrentScope: false,
hasVisibleResults: true,
},
],
},
},
);
test.describe('Unified Search - Experience Quality UX', () => {
test.beforeEach(async ({ page }) => {
await setupBasicMocks(page);
@@ -192,6 +260,29 @@ test.describe('Unified Search - Experience Quality UX', () => {
await expect(page.locator('.search__group').filter({ hasText: 'Recent' })).toHaveCount(0);
});
test('uses backend answer framing and shows overflow as secondary results without manual filters', async ({ page }) => {
await mockSearchResponses(page, (query) =>
query.includes('critical findings')
? weightedOverflowResponse
: emptyResponse(query));
await page.goto('/security/triage');
await expect(page.locator('aside.sidebar')).toBeVisible({ timeout: 15_000 });
await typeInSearch(page, 'critical findings');
await waitForResults(page);
await waitForEntityCards(page, 1);
await expect(page.locator('[data-answer-status="grounded"]')).toContainText(
/current-page findings matched first/i,
);
await expect(page.locator('.search__scope-hint')).toContainText(/weighted toward findings/i);
await expect(page.locator('[data-overflow-results]')).toContainText(/also relevant outside findings/i);
await expect(page.locator('[data-overflow-results]')).toContainText(/policy results remain relevant/i);
await expect(page.locator('[data-role="domain-filter"]')).toHaveCount(0);
await expect(page.locator('app-synthesis-panel')).toHaveCount(0);
});
test('returns structured next-step policy searches back into global search with metadata', async ({ page }) => {
const capturedRequests: Array<Record<string, unknown>> = [];
await page.route('**/search/query**', async (route) => {