diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 3a870a52..986474e5 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -321,6 +321,10 @@ "title": "CI/CD notifications enter AwoooP Timeline", "detail": "Gitea main deploys, deploy markers, and post-deploy notifications flow through the AWOOI API and AwoooP Run Timeline." }, + "sourceDossier": { + "title": "Sentry / SigNoz source dossier evidence", + "detail": "Latest sources {sources}; Sentry refs {sentry}, SigNoz refs {signoz}. Provider filters verify this, so the overall sample no longer hides source evidence." + }, "callbackEvidence": { "title": "Telegram detail / history DB truth chain", "detail": "{total} callback evidence rows are available for Run detail, history, and snapshot lookup." @@ -349,7 +353,7 @@ }, "kmGovernance": { "title": "Stale KM governance", - "detail": "{stale} KM rows are older than {days} days; Hermes drafts, owner review, and writeback remain." + "detail": "{stale}/{total} KM rows are older than {days} days ({ratio}); pending owner reviews {pending}, entries still needed for threshold {remaining}." }, "callbackBacklogDecay": { "title": "Callback legacy backlog 24h decay", @@ -535,6 +539,7 @@ }, "sources": { "dossierCoverage": "/api/v1/platform/events/dossier/coverage", + "dossierCoverageWithProviders": "/api/v1/platform/events/dossier/coverage + provider=sentry/signoz", "runsAndCicd": "/api/v1/platform/runs/list + /api/v1/platform/cicd/events", "aiRouteStatus": "/api/v1/platform/ai-route-status", "runsAndStatusChain": "/api/v1/platform/runs/list + /api/v1/platform/status-chain", @@ -545,7 +550,7 @@ }, "signal": { "metric": "sources {sources} / refs {refs}", - "detail": "missing refs {missing}, duplicates {duplicates}; Alert {alert} / Sentry {sentry} / SigNoz {signoz}" + "detail": "missing refs {missing}, duplicates {duplicates}; Alert {alert} / Sentry(provider) {sentry} / SigNoz(provider) {signoz}" }, "intake": { "metric": "Runs {runs} / linked {linked}", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 5dffc180..7d43fc8a 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -322,6 +322,10 @@ "title": "CI/CD 通知進 AwoooP 時間線", "detail": "Gitea main 推版、deploy marker、post-deploy 通知已走 AWOOI API 與 AwoooP 執行時間線。" }, + "sourceDossier": { + "title": "Sentry / SigNoz 來源卷宗證據", + "detail": "最近來源 {sources} 筆;Sentry refs {sentry}、SigNoz refs {signoz},用 provider 篩選驗證,不再被整體樣本誤判。" + }, "callbackEvidence": { "title": "Telegram 詳情 / 歷史 DB 真相鏈", "detail": "callback evidence 目前 {total} 筆,可從 Runs 反查詳情、歷史與快照。" @@ -350,7 +354,7 @@ }, "kmGovernance": { "title": "KM 陳舊資料治理", - "detail": "超過 {days} 天未更新 KM:{stale} 筆;需 Hermes 產草稿、owner 審核後回寫。" + "detail": "超過 {days} 天未更新 KM:{stale}/{total}({ratio});待 owner 審核 {pending} 筆,距離門檻還需處理 {remaining} 筆。" }, "callbackBacklogDecay": { "title": "Callback legacy backlog 24h decay", @@ -536,6 +540,7 @@ }, "sources": { "dossierCoverage": "/api/v1/platform/events/dossier/coverage", + "dossierCoverageWithProviders": "/api/v1/platform/events/dossier/coverage + provider=sentry/signoz", "runsAndCicd": "/api/v1/platform/runs/list + /api/v1/platform/cicd/events", "aiRouteStatus": "/api/v1/platform/ai-route-status", "runsAndStatusChain": "/api/v1/platform/runs/list + /api/v1/platform/status-chain", @@ -546,7 +551,7 @@ }, "signal": { "metric": "來源 {sources} / refs {refs}", - "detail": "missing refs {missing},duplicates {duplicates};Alert {alert} / Sentry {sentry} / SigNoz {signoz}" + "detail": "missing refs {missing},duplicates {duplicates};Alert {alert} / Sentry(provider) {sentry} / SigNoz(provider) {signoz}" }, "intake": { "metric": "Runs {runs} / linked {linked}", diff --git a/apps/web/src/app/[locale]/page.tsx b/apps/web/src/app/[locale]/page.tsx index c53b9f98..0e52a0b5 100644 --- a/apps/web/src/app/[locale]/page.tsx +++ b/apps/web/src/app/[locale]/page.tsx @@ -193,6 +193,8 @@ interface HomepageAutomationBriefSnapshot { aiRouteStatus?: HomepageAiRouteStatusResponse | null kmStaleCandidates?: HomepageKmStaleCandidatesResponse | null dossierCoverage?: HomepageDossierCoverageResponse | null + dossierCoverageSentry?: HomepageDossierCoverageResponse | null + dossierCoverageSignoz?: HomepageDossierCoverageResponse | null eventRecurrence?: HomepageEventRecurrenceResponse | null runsList?: HomepageRunsListResponse | null cicdEvents?: HomepageCicdEventsResponse | null @@ -774,7 +776,7 @@ export default function Home({ params }: { params: { locale: string } }) { const controller = new AbortController() const encodedProjectId = encodeURIComponent('awoooi') let isCancelled = false - let pendingRequests = 8 + let pendingRequests = 10 const finishRequest = () => { pendingRequests -= 1 if (pendingRequests <= 0 && !isCancelled) { @@ -829,6 +831,20 @@ export default function Home({ params }: { params: { locale: string } }) { controller.signal ) ) + loadBriefPart( + 'dossierCoverageSentry', + fetchHomepageJson( + `${API_BASE}/api/v1/platform/events/dossier/coverage?project_id=${encodedProjectId}&provider=sentry&limit=100`, + controller.signal + ) + ) + loadBriefPart( + 'dossierCoverageSignoz', + fetchHomepageJson( + `${API_BASE}/api/v1/platform/events/dossier/coverage?project_id=${encodedProjectId}&provider=signoz&limit=100`, + controller.signal + ) + ) loadBriefPart( 'eventRecurrence', fetchHomepageJson( @@ -875,6 +891,8 @@ export default function Home({ params }: { params: { locale: string } }) { const aiRouteStatusLoaded = hasAutomationBriefPart('aiRouteStatus') const kmStaleCandidatesLoaded = hasAutomationBriefPart('kmStaleCandidates') const dossierCoverageLoaded = hasAutomationBriefPart('dossierCoverage') + const dossierCoverageSentryLoaded = hasAutomationBriefPart('dossierCoverageSentry') + const dossierCoverageSignozLoaded = hasAutomationBriefPart('dossierCoverageSignoz') const eventRecurrenceLoaded = hasAutomationBriefPart('eventRecurrence') const runsListLoaded = hasAutomationBriefPart('runsList') const cicdEventsLoaded = hasAutomationBriefPart('cicdEvents') @@ -943,6 +961,12 @@ export default function Home({ params }: { params: { locale: string } }) { const hasKmStaleCandidates = automationBrief.kmStaleCandidates !== undefined && automationBrief.kmStaleCandidates !== null const canClaimFullAutoRepair = automationQualityAvailable && Boolean(automationQuality?.production_claim?.can_claim_full_auto_repair) const dossierCoverageSummary = automationBrief.dossierCoverage?.summary ?? null + const dossierCoverageSentrySummary = automationBrief.dossierCoverageSentry?.summary ?? null + const dossierCoverageSignozSummary = automationBrief.dossierCoverageSignoz?.summary ?? null + const dossierSentryRefs = dossierCoverageSentrySummary?.sentry_ref_total ?? dossierCoverageSummary?.sentry_ref_total + const dossierSignozRefs = dossierCoverageSignozSummary?.signoz_ref_total ?? dossierCoverageSummary?.signoz_ref_total + const dossierProviderRefsLoaded = dossierCoverageSentryLoaded && dossierCoverageSignozLoaded + const dossierProviderRefsLive = (dossierSentryRefs ?? 0) > 0 || (dossierSignozRefs ?? 0) > 0 const recurrenceSummary = automationBrief.eventRecurrence?.summary ?? null const recentRuns = automationBrief.runsList?.runs ?? [] const latestRun = recentRuns[0] @@ -999,6 +1023,10 @@ export default function Home({ params }: { params: { locale: string } }) { const cicdNeedsAttention = cicdItems.filter(item => item.needs_attention).length const kmOwnerReviewBurndown = automationBrief.kmOwnerReviewBurndown ?? null const kmBurndownSnapshot = kmOwnerReviewBurndown?.current_snapshot ?? null + const kmStaleDisplayCount = kmBurndownSnapshot?.stale_count ?? kmStaleTotal + const kmTotalDisplayCount = kmBurndownSnapshot?.total_count + const kmEntriesToThreshold = kmOwnerReviewBurndown?.entries_to_threshold + const kmGovernanceLoaded = kmOwnerReviewBurndownLoaded || kmStaleCandidatesLoaded const staleRatioLabel = typeof kmBurndownSnapshot?.stale_ratio === 'number' ? `${(kmBurndownSnapshot.stale_ratio * 100).toFixed(1)}%` : kmOwnerReviewBurndownLoaded ? unavailableValue : loadingStatus @@ -1029,6 +1057,22 @@ export default function Home({ params }: { params: { locale: string } }) { href: `/${locale}/awooop/runs`, tone: 'live', }, + { + key: 'sourceDossier', + title: tDashboard('automationDelivery.delivered.sourceDossier.title'), + status: !dossierProviderRefsLoaded + ? loadingStatus + : dossierProviderRefsLive + ? tDashboard('automationDelivery.status.live') + : tDashboard('automationDelivery.status.watching'), + detail: tDashboard('automationDelivery.delivered.sourceDossier.detail', { + sources: formatAutomationNumber(dossierCoverageSummary?.source_count, dossierCoverageLoaded), + sentry: formatAutomationNumber(dossierSentryRefs, dossierCoverageSentryLoaded || dossierCoverageLoaded), + signoz: formatAutomationNumber(dossierSignozRefs, dossierCoverageSignozLoaded || dossierCoverageLoaded), + }), + href: `/${locale}/alerts`, + tone: dossierProviderRefsLive ? 'live' : dossierProviderRefsLoaded ? 'watching' : 'progress', + }, { key: 'callbackEvidence', title: tDashboard('automationDelivery.delivered.callbackEvidence.title'), @@ -1133,19 +1177,23 @@ export default function Home({ params }: { params: { locale: string } }) { { key: 'kmGovernance', title: tDashboard('automationDelivery.remaining.kmGovernance.title'), - status: !kmStaleCandidatesLoaded + status: !kmGovernanceLoaded ? loadingStatus - : typeof kmStaleTotal === 'number' && kmStaleTotal > 0 + : typeof kmStaleDisplayCount === 'number' && kmStaleDisplayCount > 0 ? tDashboard('automationDelivery.status.progress') : hasKmStaleCandidates ? tDashboard('automationDelivery.status.live') : unavailableStatus, detail: tDashboard('automationDelivery.remaining.kmGovernance.detail', { - stale: formatAutomationNumber(kmStaleTotal, kmStaleCandidatesLoaded), - days: formatAutomationNumber(kmStaleThreshold, kmStaleCandidatesLoaded), + stale: formatAutomationNumber(kmStaleDisplayCount, kmGovernanceLoaded), + total: formatAutomationNumber(kmTotalDisplayCount, kmOwnerReviewBurndownLoaded), + ratio: staleRatioLabel, + pending: formatAutomationNumber(kmOwnerReviewBurndown?.pending_owner_reviews, kmOwnerReviewBurndownLoaded), + remaining: formatAutomationNumber(kmEntriesToThreshold, kmOwnerReviewBurndownLoaded), + days: formatAutomationNumber(kmStaleThreshold ?? kmBurndownSnapshot?.stale_days, kmGovernanceLoaded), }), href: `/${locale}/awooop/work-items?project_id=awoooi`, - tone: typeof kmStaleTotal === 'number' && kmStaleTotal > 0 ? 'progress' : hasKmStaleCandidates ? 'live' : 'watching', + tone: typeof kmStaleDisplayCount === 'number' && kmStaleDisplayCount > 0 ? 'progress' : hasKmStaleCandidates ? 'live' : 'watching', }, { key: 'callbackBacklogDecay', @@ -1243,11 +1291,16 @@ export default function Home({ params }: { params: { locale: string } }) { missing: formatAutomationNumber(dossierCoverageSummary?.missing_source_refs_total, dossierCoverageLoaded), duplicates: formatAutomationNumber(dossierCoverageSummary?.duplicate_total, dossierCoverageLoaded), alert: formatAutomationNumber(dossierCoverageSummary?.alert_ref_total, dossierCoverageLoaded), - sentry: formatAutomationNumber(dossierCoverageSummary?.sentry_ref_total, dossierCoverageLoaded), - signoz: formatAutomationNumber(dossierCoverageSummary?.signoz_ref_total, dossierCoverageLoaded), + sentry: formatAutomationNumber(dossierSentryRefs, dossierCoverageSentryLoaded || dossierCoverageLoaded), + signoz: formatAutomationNumber(dossierSignozRefs, dossierCoverageSignozLoaded || dossierCoverageLoaded), }), - source: tDashboard('automationDiagrams.workspace.liveEvidence.sources.dossierCoverage'), - updated: formatLiveEvidenceTime(dossierCoverageSummary?.latest_received_at, dossierCoverageLoaded), + source: tDashboard('automationDiagrams.workspace.liveEvidence.sources.dossierCoverageWithProviders'), + updated: formatLiveEvidenceTime( + dossierCoverageSummary?.latest_received_at + ?? dossierCoverageSentrySummary?.latest_received_at + ?? dossierCoverageSignozSummary?.latest_received_at, + dossierCoverageLoaded || dossierProviderRefsLoaded + ), }, }, { @@ -1430,13 +1483,13 @@ export default function Home({ params }: { params: { locale: string } }) { nextAction: tDashboard('automationDiagrams.workspace.inspector.stages.verify.nextAction'), liveEvidence: { metric: tDashboard('automationDiagrams.workspace.liveEvidence.verify.metric', { - stale: formatAutomationNumber(kmBurndownSnapshot?.stale_count ?? kmStaleTotal, kmOwnerReviewBurndownLoaded || kmStaleCandidatesLoaded), + stale: formatAutomationNumber(kmStaleDisplayCount, kmGovernanceLoaded), ratio: staleRatioLabel, }), detail: tDashboard('automationDiagrams.workspace.liveEvidence.verify.detail', { pending: formatAutomationNumber(kmOwnerReviewBurndown?.pending_owner_reviews, kmOwnerReviewBurndownLoaded), completed: formatAutomationNumber(kmOwnerReviewBurndown?.completed_owner_reviews, kmOwnerReviewBurndownLoaded), - remaining: formatAutomationNumber(kmOwnerReviewBurndown?.entries_to_threshold, kmOwnerReviewBurndownLoaded), + remaining: formatAutomationNumber(kmEntriesToThreshold, kmOwnerReviewBurndownLoaded), }), source: tDashboard('automationDiagrams.workspace.liveEvidence.sources.kmBurndown'), updated: formatLiveEvidenceTime(kmOwnerReviewBurndown?.generated_at, kmOwnerReviewBurndownLoaded),