diff --git a/apps/web/src/app/[locale]/awooop/work-items/page.tsx b/apps/web/src/app/[locale]/awooop/work-items/page.tsx index ddea2eb4..0f9f589a 100644 --- a/apps/web/src/app/[locale]/awooop/work-items/page.tsx +++ b/apps/web/src/app/[locale]/awooop/work-items/page.tsx @@ -1105,6 +1105,11 @@ async function fetchJson(url: string, timeoutMs = 8000): Promise { } } +function withProjectId(url: string, projectId: string) { + const separator = url.includes("?") ? "&" : "?"; + return `${url}${separator}project_id=${encodeURIComponent(projectId)}`; +} + async function postJson( url: string, body: Record, @@ -1146,8 +1151,14 @@ function recurrenceOpenItems(recurrence: RecurrenceResponse | null) { return (recurrence?.items ?? []).filter((item) => item.work_item?.status === "open"); } +const CANONICAL_INCIDENT_ID_RE = /^INC-\d{8}-[A-Z0-9]{4,}$/; + +function isCanonicalIncidentId(candidate: string | null | undefined) { + return Boolean(candidate?.trim() && CANONICAL_INCIDENT_ID_RE.test(candidate.trim())); +} + function firstIncidentId(...candidates: Array) { - return candidates.find((candidate) => Boolean(candidate?.trim()))?.trim() ?? null; + return candidates.find(isCanonicalIncidentId)?.trim() ?? null; } function parseRepairCandidateDraftWorkItemId( @@ -6105,22 +6116,46 @@ export default function AwoooPWorkItemsPage() { setLoading(true); const encodedProjectId = encodeURIComponent(projectId); const qualityUrl = `${API_BASE}/api/v1/platform/truth-chain/quality/summary?project_id=${encodedProjectId}&hours=24&limit=30`; - const governanceEventsUrl = `${API_BASE}/api/v1/ai/governance/events?event_type=knowledge_degradation&event_type=governance_slo_data_gap&status=unresolved&size=10`; - const governanceQueueUrl = `${API_BASE}/api/v1/ai/governance/queue?dispatch_status=pending&size=10`; - const governanceKnowledgeQueueUrl = `${API_BASE}/api/v1/ai/governance/queue?dispatch_status=all&event_type=knowledge_degradation&size=20`; - const knowledgeReviewDraftsUrl = `${API_BASE}/api/v1/knowledge?entry_type=auto_runbook&status=review&q=${encodeURIComponent("KM healthcheck")}&limit=100`; - const knowledgeReviewDedupeUrl = `${API_BASE}/api/v1/ai/governance/km-review-drafts/dedupe?limit=100`; + const governanceEventsUrl = withProjectId( + `${API_BASE}/api/v1/ai/governance/events?event_type=knowledge_degradation&event_type=governance_slo_data_gap&status=unresolved&size=10`, + projectId + ); + const governanceQueueUrl = withProjectId( + `${API_BASE}/api/v1/ai/governance/queue?dispatch_status=pending&size=10`, + projectId + ); + const governanceKnowledgeQueueUrl = withProjectId( + `${API_BASE}/api/v1/ai/governance/queue?dispatch_status=all&event_type=knowledge_degradation&size=20`, + projectId + ); + const knowledgeReviewDraftsUrl = withProjectId( + `${API_BASE}/api/v1/knowledge?entry_type=auto_runbook&status=review&q=${encodeURIComponent("KM healthcheck")}&limit=100`, + projectId + ); + const knowledgeReviewDedupeUrl = withProjectId( + `${API_BASE}/api/v1/ai/governance/km-review-drafts/dedupe?limit=100`, + projectId + ); const knowledgeStaleCandidatesUrl = `${API_BASE}/api/v1/ai/governance/km-stale-candidates?project_id=${encodedProjectId}&limit=20`; const knowledgeStaleOwnerReviewsUrl = `${API_BASE}/api/v1/ai/governance/km-stale-owner-reviews?project_id=${encodedProjectId}&dispatch_status=pending&limit=30`; const knowledgeStaleOwnerReviewBurnDownUrl = `${API_BASE}/api/v1/ai/governance/km-stale-owner-review-burndown?project_id=${encodedProjectId}&limit=20`; const knowledgeStaleOwnerReviewCompletionQueueUrl = `${API_BASE}/api/v1/ai/governance/km-stale-owner-review-completion-queue?project_id=${encodedProjectId}&status_bucket=all&limit=30`; const channelEventsUrl = `${API_BASE}/api/v1/platform/events/recent?project_id=${encodedProjectId}&provider_prefix=alertmanager&limit=20`; const recurrenceUrl = `${API_BASE}/api/v1/platform/events/dossier/recurrence?project_id=${encodedProjectId}&limit=100`; - const sloUrl = `${API_BASE}/api/v1/ai/slo`; - const remediationHistoryUrl = `${API_BASE}/api/v1/ai/slo/remediation/history?limit=80`; - const driftFingerprintUrl = `${API_BASE}/api/v1/drift/fingerprints/state?namespace=awoooi-prod`; + const sloUrl = withProjectId(`${API_BASE}/api/v1/ai/slo`, projectId); + const remediationHistoryUrl = withProjectId( + `${API_BASE}/api/v1/ai/slo/remediation/history?limit=80`, + projectId + ); + const driftFingerprintUrl = withProjectId( + `${API_BASE}/api/v1/drift/fingerprints/state?namespace=awoooi-prod`, + projectId + ); const callbackRepliesUrl = `${API_BASE}/api/v1/platform/runs/callback-replies?project_id=${encodedProjectId}&per_page=100`; - const aiRouteStatusUrl = `${API_BASE}/api/v1/platform/ai-route-status?workload_type=deep_rca`; + const aiRouteStatusUrl = withProjectId( + `${API_BASE}/api/v1/platform/ai-route-status?workload_type=deep_rca`, + projectId + ); const [ quality, @@ -6177,7 +6212,10 @@ export default function AwoooPWorkItemsPage() { const timelineIncidentId = statusChain?.source_id ?? statusChainIncidentId; const incidentTimeline = timelineIncidentId ? await fetchJson( - `${API_BASE}/api/v1/incidents/${encodeURIComponent(timelineIncidentId)}/timeline`, + withProjectId( + `${API_BASE}/api/v1/incidents/${encodeURIComponent(timelineIncidentId)}/timeline`, + projectId + ), 12000 ) : null;