Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import {
QueueHealthCard,
type MaintainerQueueHealth,
} from "@/components/site/app-panels/queue-health-card";
import {
SlopDuplicateCard,
type SlopDuplicateSignal,
} from "@/components/site/app-panels/slop-duplicate-card";
import { MaintainerSettings } from "@/components/site/app-panels/maintainer-settings";
import { OnboardingPreviewCard } from "@/components/site/app-panels/onboarding-preview-card";
import { CheckRunReadinessTable } from "@/components/site/check-run-readiness-table";
Expand Down Expand Up @@ -92,6 +96,7 @@ type MaintainerDashboard = {
topContributors: MaintainerTopContributor[];
gateOutcomeBreakdown: GateOutcomeCardData;
queueHealth?: MaintainerQueueHealth;
slopDuplicate?: SlopDuplicateSignal;
};
};

Expand Down Expand Up @@ -388,6 +393,8 @@ function MaintainerDashboardView({

<QueueHealthCard queueHealth={data.qualityDashboard.queueHealth} />

<SlopDuplicateCard signal={data.qualityDashboard.slopDuplicate} />

<ContributorQualityTable topContributors={data.qualityDashboard.topContributors} />

<ActivationPreview reviewability={data.reviewability} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";

import { SlopDuplicateCard } from "@/components/site/app-panels/slop-duplicate-card";

describe("SlopDuplicateCard", () => {
it("renders both signal rows with rates and flagged/total counts when populated", () => {
render(
<SlopDuplicateCard
signal={{
openPullRequests: 20,
slopFlaggedPullRequests: 3,
duplicateFlaggedPullRequests: 8,
slopRate: 0.15,
duplicateRate: 0.4,
}}
/>,
);
expect(screen.getByText("Slop-flagged")).toBeTruthy();
expect(screen.getByText("Duplicate-flagged")).toBeTruthy();
expect(screen.getByText("15%")).toBeTruthy();
expect(screen.getByText("40%")).toBeTruthy();
expect(screen.getByText(/3 of 20 open PRs/)).toBeTruthy();
expect(screen.getByText(/8 of 20 open PRs/)).toBeTruthy();
});

it("renders an em-dash when a rate is null (unassessed)", () => {
render(
<SlopDuplicateCard
signal={{
openPullRequests: 5,
slopFlaggedPullRequests: 0,
duplicateFlaggedPullRequests: 1,
slopRate: null,
duplicateRate: 0.2,
}}
/>,
);
expect(screen.getByText("—")).toBeTruthy();
expect(screen.getByText("no data")).toBeTruthy();
});

it("shows the 'queue is clear' empty state when there are no open PRs", () => {
render(
<SlopDuplicateCard
signal={{
openPullRequests: 0,
slopFlaggedPullRequests: 0,
duplicateFlaggedPullRequests: 0,
slopRate: null,
duplicateRate: null,
}}
/>,
);
expect(screen.getByText("Queue is clear")).toBeTruthy();
expect(screen.queryByText("Slop-flagged")).toBeNull();
});

it("shows the 'not yet available' empty state when the signal is absent", () => {
render(<SlopDuplicateCard />);
expect(screen.getByText("Not yet available")).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { AnalyticsCardShell } from "@/components/site/app-panels/analytics-card-shell";
import { StatusPill, type Status } from "@/components/site/control-primitives";
import { cn } from "@/lib/utils";

/** Aggregate slop + duplicate signal for the maintainer quality dashboard (#2202): the share of the maintainer's
* open PRs carrying an elevated/high deterministic slop band and the share sitting in a high-risk duplicate
* cluster, each as a rate of the open-PR total. Display slice over the dashboard payload's `slopDuplicate`
* aggregate — observable rates + bands, never raw scores. Degrades to an empty state when the queue is clear
* or the field is absent. (Current-window rates; a time-series trend is a follow-up per the issue thread.) */
export type SlopDuplicateSignal = {
openPullRequests: number;
slopFlaggedPullRequests: number;
duplicateFlaggedPullRequests: number;
slopRate: number | null;
duplicateRate: number | null;
};

/** Lower is healthier — a small flagged share is good; a large one is a burden signal. */
function rateStatus(rate: number | null): Status {
if (rate === null) return "info";
if (rate < 0.1) return "ready";
if (rate < 0.3) return "warn";
return "degraded";
}

function rateBandLabel(rate: number | null): string {
if (rate === null) return "no data";
if (rate < 0.1) return "low";
if (rate < 0.3) return "watch";
return "high";
}

function SignalRow({
label,
rate,
flagged,
total,
bar,
}: {
label: string;
rate: number | null;
flagged: number;
total: number;
bar: string;
}) {
const pct = rate === null ? null : Math.round(rate * 100);
return (
<div className="space-y-1.5">
<div className="flex items-center justify-between gap-3 text-token-sm">
<span className="font-medium text-foreground">{label}</span>
<span className="font-mono text-token-xs text-muted-foreground">
{pct === null ? "—" : `${pct}%`}
</span>
</div>
<div className="h-2 overflow-hidden rounded-full bg-border" aria-hidden>
{pct === null ? null : <div className={cn("h-full", bar)} style={{ width: `${pct}%` }} />}
</div>
<div className="flex items-center justify-between font-mono text-token-2xs text-muted-foreground">
<span>
{flagged} of {total} open PR{total === 1 ? "" : "s"}
</span>
<StatusPill status={rateStatus(rate)}>{rateBandLabel(rate)}</StatusPill>
</div>
</div>
);
}

export function SlopDuplicateCard({ signal }: { signal?: SlopDuplicateSignal }) {
if (!signal || signal.openPullRequests === 0) {
return (
<AnalyticsCardShell
title="Slop & duplicate signal"
description="Share of open PRs flagged as slop or in a high-risk duplicate cluster, across your repos."
state="empty"
emptyTitle={signal ? "Queue is clear" : "Not yet available"}
emptyHint={
signal
? "No open pull requests across the shaped repos in this window."
: "The slop + duplicate signal appears once the maintainer dashboard payload includes the aggregate."
}
/>
);
}

return (
<AnalyticsCardShell
title="Slop & duplicate signal"
description="Share of open PRs flagged as slop or in a high-risk duplicate cluster, across your repos."
state="ready"
>
<div className="space-y-4">
<SignalRow
label="Slop-flagged"
rate={signal.slopRate}
flagged={signal.slopFlaggedPullRequests}
total={signal.openPullRequests}
bar="bg-warning"
/>
<SignalRow
label="Duplicate-flagged"
rate={signal.duplicateRate}
flagged={signal.duplicateFlaggedPullRequests}
total={signal.openPullRequests}
bar="bg-danger"
/>
</div>
</AnalyticsCardShell>
);
}
20 changes: 20 additions & 0 deletions src/services/maintainer-quality-dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ export type MaintainerQualityDashboard = {
ageBuckets: { under7Days: number; days7To30: number; over30Days: number };
bandCounts: Record<QueueHealth["level"], number>;
};
/** Aggregate slop + duplicate signal across the SHAPED repos' open PRs (#2202): how many open PRs carry an
* elevated/high deterministic slop band, how many sit in a high-risk duplicate cluster, and each as a rate
* of the open-PR total (null when there are no open PRs). Observable counts + rates, never raw scores. */
slopDuplicate: {
openPullRequests: number;
slopFlaggedPullRequests: number;
duplicateFlaggedPullRequests: number;
slopRate: number | null;
duplicateRate: number | null;
};
summary: string;
};

Expand Down Expand Up @@ -91,6 +101,7 @@ export function buildMaintainerQualityDashboard(args: { repos: MaintainerQuality
let openPrs = 0;
let duplicatePrRisk = 0;
let missingLinkedIssue = 0;
let slopFlaggedPrs = 0;
const queueHealthAggregate = {
openPullRequests: 0,
stalePullRequests: 0,
Expand Down Expand Up @@ -141,6 +152,8 @@ export function buildMaintainerQualityDashboard(args: { repos: MaintainerQuality
const inHighRiskCluster = highRiskPrNumbers.has(pr.number);
if (pr.linkedIssues.length === 0) missingLinkedIssue += 1;
if (inHighRiskCluster) duplicatePrRisk += 1;
// #2202: an "elevated" or "high" deterministic slop band is the slop flag (null/absent = not assessed).
if (pr.slopBand === "elevated" || pr.slopBand === "high") slopFlaggedPrs += 1;
const linkedToRealIssue = pr.linkedIssues.some((number) => realIssueNumbers.has(number));
const author = pr.authorLogin ?? "unknown";
const tally = contributorTotals.get(author) ?? { open: 0, clean: 0 };
Expand Down Expand Up @@ -171,6 +184,13 @@ export function buildMaintainerQualityDashboard(args: { repos: MaintainerQuality
topContributors,
qualitySignals: { openPrs, duplicatePrRisk, missingLinkedIssue },
queueHealth: queueHealthAggregate,
slopDuplicate: {
openPullRequests: openPrs,
slopFlaggedPullRequests: slopFlaggedPrs,
duplicateFlaggedPullRequests: duplicatePrRisk,
slopRate: openPrs > 0 ? slopFlaggedPrs / openPrs : null,
duplicateRate: openPrs > 0 ? duplicatePrRisk / openPrs : null,
},
summary,
};
}
20 changes: 20 additions & 0 deletions test/unit/maintainer-quality-dashboard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,26 @@ describe("buildMaintainerQualityDashboard", () => {
expect(JSON.stringify(dashboard)).not.toMatch(/"burdenScore"/);
});

it("aggregates the slop + duplicate signal and its rates, failing safe to null rates with no open PRs (#2202)", () => {
const flagged = buildMaintainerQualityDashboard({
repos: [input({ pullRequests: [pr(1), pr(2, { slopBand: "high" })] })],
generatedAt: "2026-06-14T00:00:00.000Z",
});
expect(flagged.slopDuplicate.openPullRequests).toBe(2);
expect(flagged.slopDuplicate.slopFlaggedPullRequests).toBe(1);
expect(flagged.slopDuplicate.slopRate).toBeCloseTo(0.5);
expect(flagged.slopDuplicate.duplicateRate).not.toBeNull();

// No open PRs → the rates divide-guard to null rather than 0/0.
const empty = buildMaintainerQualityDashboard({
repos: [input({ pullRequests: [] })],
generatedAt: "2026-06-14T00:00:00.000Z",
});
expect(empty.slopDuplicate.openPullRequests).toBe(0);
expect(empty.slopDuplicate.slopRate).toBeNull();
expect(empty.slopDuplicate.duplicateRate).toBeNull();
});

it("counts PRs without a linked issue toward the missing-linked-issue signal", () => {
const dashboard = buildMaintainerQualityDashboard({
repos: [input({ pullRequests: [pr(1, { linkedIssues: [] }), pr(2, { linkedIssues: [5] })] })],
Expand Down
Loading