diff --git a/platform/lib/queries/org-summary.ts b/platform/lib/queries/org-summary.ts index 048a6ba..33a7ae1 100644 --- a/platform/lib/queries/org-summary.ts +++ b/platform/lib/queries/org-summary.ts @@ -124,10 +124,9 @@ function simpleAvg(values: Array): number | null { return valid.reduce((s, v) => s + v, 0) / valid.length; } -/** Build the org-wide sparkline by summing or averaging per-position across repos. */ +/** Build the org-wide sparkline by averaging per-position across repos. */ function buildOrgSparkline( repos: RepoSummary[], - mode: "sum" | "avg", accessor: (r: RepoSummary) => number[], ): number[] { const maxLen = Math.max(...repos.map((r) => accessor(r).length), 0); @@ -146,7 +145,7 @@ function buildOrgSparkline( count++; } } - result.push(mode === "sum" ? sum : count > 0 ? sum / count : 0); + result.push(count > 0 ? sum / count : 0); } return result; } @@ -207,10 +206,8 @@ export function computeOrgPulse( ? aiAdoptionPct - previousTotals.aiPct! : null; - // Sparklines - const commitSparkline = buildOrgSparkline(repos, "sum", () => []); - // Use stabilization sparklines from repos - const stabSparkline = buildOrgSparkline(repos, "avg", (r) => r.sparkline); + // Sparkline — from each repo's own stabilization history. + const stabSparkline = buildOrgSparkline(repos, (r) => r.sparkline); return { totalCommits, @@ -224,9 +221,7 @@ export function computeOrgPulse( aiAdoptionPct, aiAdoptionDelta, sparklines: { - commits: commitSparkline, stabilization: stabSparkline, - aiAdoption: [], }, }; } diff --git a/platform/lib/queries/temporal.ts b/platform/lib/queries/temporal.ts index c47ac8f..f7daf9c 100644 --- a/platform/lib/queries/temporal.ts +++ b/platform/lib/queries/temporal.ts @@ -92,7 +92,7 @@ export async function getRepoTimeSeries( const { data } = await supabase .from("metrics") .select( - "created_at, stabilization_ratio, revert_rate, churn_events, commits_total, ai_detection_coverage_pct, pr_merged_count, pr_single_pass_rate, fix_latency_median_hours, cascade_rate", + "created_at, stabilization_ratio, revert_rate, churn_events, commits_total, ai_detection_coverage_pct", ) .eq("repository_id", repositoryId) .eq("window_days", windowDays) @@ -106,10 +106,6 @@ export async function getRepoTimeSeries( churn_events: row.churn_events, commits_total: row.commits_total, ai_detection_coverage_pct: row.ai_detection_coverage_pct, - pr_merged_count: row.pr_merged_count, - pr_single_pass_rate: row.pr_single_pass_rate, - fix_latency_median_hours: row.fix_latency_median_hours, - cascade_rate: row.cascade_rate, })); } diff --git a/platform/lib/translations.ts b/platform/lib/translations.ts index 4952426..972d5b2 100644 --- a/platform/lib/translations.ts +++ b/platform/lib/translations.ts @@ -753,7 +753,7 @@ export const translations = { aiExposure: { title: "Shadow AI Exposure", subtitle: - "How much AI-assisted code is being produced — and how much is attributed.", + "How much AI-assisted code is being produced — and how much is attributed. The per-repo breakdown behind the dashboard's Attribution Gap card.", empty: "No origin data yet. Run the CLI on a repository to start measuring.", summary: { @@ -2161,7 +2161,7 @@ export const translations = { aiExposure: { title: "Exposição a Shadow AI", subtitle: - "Quanto código assistido por IA está sendo produzido — e quanto está atribuído.", + 'Quanto código assistido por IA está sendo produzido — e quanto está atribuído. O detalhamento por repositório por trás do card "Lacuna de Atribuição" do dashboard.', empty: "Ainda não há dados de origem. Rode a CLI em um repositório para começar a medir.", summary: { diff --git a/platform/src/app/[tenant]/repos/[repoName]/charts.tsx b/platform/src/app/[tenant]/repos/[repoName]/charts.tsx index 38c862d..67e24e8 100644 --- a/platform/src/app/[tenant]/repos/[repoName]/charts.tsx +++ b/platform/src/app/[tenant]/repos/[repoName]/charts.tsx @@ -788,11 +788,9 @@ export function RepoCharts({ const chartData = timeSeries.map((p) => ({ date: p.date, stabilization_ratio: p.stabilization_ratio, - revert_rate: p.revert_rate, churn_events: p.churn_events, commits_total: p.commits_total, ai_detection_coverage_pct: p.ai_detection_coverage_pct, - cascade_rate: p.cascade_rate, })); const hasAI = timeSeries.some( diff --git a/platform/src/app/[tenant]/repos/[repoName]/investment-hotspots.tsx b/platform/src/app/[tenant]/repos/[repoName]/investment-hotspots.tsx index 13f2996..7991777 100644 --- a/platform/src/app/[tenant]/repos/[repoName]/investment-hotspots.tsx +++ b/platform/src/app/[tenant]/repos/[repoName]/investment-hotspots.tsx @@ -1,6 +1,6 @@ -'use client'; +"use client"; -import { AlertOctagon, Bug, FolderOpen, Link2 } from 'lucide-react'; +import { AlertOctagon, Bug, FolderOpen, Link2 } from "lucide-react"; import { Card, @@ -8,38 +8,38 @@ import { CardDescription, CardHeader, CardTitle, -} from '@/components/ui/card'; -import { useTranslation } from '@/hooks/useTranslation'; -import { cn } from '@/lib/utils'; +} from "@/components/ui/card"; +import { useTranslation } from "@/hooks/useTranslation"; +import { cn } from "@/lib/utils"; import type { HotspotSeverity, InvestmentHotspot, InvestmentHotspots as InvestmentHotspotsData, -} from '@/types/invest-here'; +} from "@/types/invest-here"; interface InvestmentHotspotsProps { data: InvestmentHotspotsData; } const severityColor: Record = { - high: 'bg-signal-red/10 text-signal-red border-signal-red/30', - medium: 'bg-signal-yellow/10 text-signal-yellow border-signal-yellow/30', - low: 'bg-muted text-muted-foreground border-border', + high: "bg-signal-red/10 text-signal-red border-signal-red/30", + medium: "bg-signal-yellow/10 text-signal-yellow border-signal-yellow/30", + low: "bg-muted text-muted-foreground border-border", }; function SeverityBadge({ severity }: { severity: HotspotSeverity }) { const { t } = useTranslation(); const label = - severity === 'high' - ? t('investHere.severityHigh') - : severity === 'medium' - ? t('investHere.severityMedium') - : t('investHere.severityLow'); + severity === "high" + ? t("investHere.severityHigh") + : severity === "medium" + ? t("investHere.severityMedium") + : t("investHere.severityLow"); return ( @@ -51,19 +51,21 @@ function SeverityBadge({ severity }: { severity: HotspotSeverity }) { function HotspotRow({ hotspot }: { hotspot: InvestmentHotspot }) { const { t } = useTranslation(); - if (hotspot.kind === 'weak_directory') { + if (hotspot.kind === "weak_directory") { return (

- {t('investHere.weakDirectoryTitle', { directory: hotspot.directory })} + {t("investHere.weakDirectoryTitle", { + directory: hotspot.directory, + })}

- {t('investHere.weakDirectoryReason', { + {t("investHere.weakDirectoryReason", { ratio: (hotspot.stabilizationRatio * 100).toFixed(0), files: hotspot.filesTouched, churn: hotspot.churnEvents, @@ -74,19 +76,19 @@ function HotspotRow({ hotspot }: { hotspot: InvestmentHotspot }) { ); } - if (hotspot.kind === 'tight_coupling') { + if (hotspot.kind === "tight_coupling") { return (

- {t('investHere.tightCouplingTitle')} + {t("investHere.tightCouplingTitle")}

- {t('investHere.tightCouplingReason', { + {t("investHere.tightCouplingReason", { fileA: hotspot.fileA, fileB: hotspot.fileB, rate: (hotspot.couplingRate * 100).toFixed(0), @@ -98,20 +100,28 @@ function HotspotRow({ hotspot }: { hotspot: InvestmentHotspot }) { ); } - // fix_magnet + // fix_magnet — translate the raw origin enum, matching charts.tsx's + // originLabels convention, instead of interpolating "AI_ASSISTED" as-is. + const originLabel = + hotspot.origin === "HUMAN" + ? t("repoCharts.origin.labels.human") + : hotspot.origin === "AI_ASSISTED" + ? t("repoCharts.origin.labels.ai") + : t("repoCharts.origin.labels.bot"); + return (

- {t('investHere.fixMagnetTitle', { origin: hotspot.origin })} + {t("investHere.fixMagnetTitle", { origin: originLabel })}

- {t('investHere.fixMagnetReason', { - origin: hotspot.origin, + {t("investHere.fixMagnetReason", { + origin: originLabel, codeShare: hotspot.codeSharePct.toFixed(0), fixShare: hotspot.fixSharePct.toFixed(0), disp: hotspot.disproportionality.toFixed(1), @@ -130,13 +140,13 @@ export function InvestmentHotspots({ data }: InvestmentHotspotsProps) { return ( - {t('investHere.title')} - {t('investHere.subtitle')} + {t("investHere.title")} + {t("investHere.subtitle")}

- {t('investHere.empty')} + {t("investHere.empty")}
@@ -146,8 +156,8 @@ export function InvestmentHotspots({ data }: InvestmentHotspotsProps) { return ( - {t('investHere.title')} - {t('investHere.subtitle')} + {t("investHere.title")} + {t("investHere.subtitle")}
@@ -156,7 +166,7 @@ export function InvestmentHotspots({ data }: InvestmentHotspotsProps) { ))}

- {t('investHere.hypothesisNote')} + {t("investHere.hypothesisNote")}

diff --git a/platform/src/types/org-summary.ts b/platform/src/types/org-summary.ts index 631f04a..a621c58 100644 --- a/platform/src/types/org-summary.ts +++ b/platform/src/types/org-summary.ts @@ -18,9 +18,7 @@ export interface OrgPulse { aiAdoptionPct: number | null; aiAdoptionDelta: number | null; sparklines: { - commits: number[]; stabilization: number[]; - aiAdoption: number[]; }; } diff --git a/platform/src/types/temporal.ts b/platform/src/types/temporal.ts index 4f68159..b22a079 100644 --- a/platform/src/types/temporal.ts +++ b/platform/src/types/temporal.ts @@ -10,10 +10,6 @@ export interface TimeSeriesPoint { churn_events: number | null; commits_total: number | null; ai_detection_coverage_pct: number | null; - pr_merged_count: number | null; - pr_single_pass_rate: number | null; - fix_latency_median_hours: number | null; - cascade_rate: number | null; } /** Summary of a repo's current state + trend. */