diff --git a/apps/loopover-ui/src/components/site/audit-feed-model.ts b/apps/loopover-ui/src/components/site/audit-feed-model.ts
index 4efc4721c3..e934099bf0 100644
--- a/apps/loopover-ui/src/components/site/audit-feed-model.ts
+++ b/apps/loopover-ui/src/components/site/audit-feed-model.ts
@@ -125,5 +125,8 @@ export function skipReasonTone(reason: string): "ready" | "info" | "warn" | "deg
if (reason === "bot_author" || reason === "not_official_gittensor_miner") return "info";
if (reason === "surface_off" || reason === "maintainer_author") return "warn";
if (reason === "miner_detection_unavailable" || reason === "missing_author") return "degraded";
- return "ready";
+ // `reason` is a plain string at the API boundary (same widening as contributor-quality-table-model's
+ // `band: string`), so an unrecognized/legacy value can reach here. Degrade it to the neutral "info"
+ // pill — never "ready", which would render an unclassified skip as green/healthy.
+ return "info";
}
diff --git a/apps/loopover-ui/src/components/site/audit-feed.test.tsx b/apps/loopover-ui/src/components/site/audit-feed.test.tsx
index 563d20bb0c..202777866d 100644
--- a/apps/loopover-ui/src/components/site/audit-feed.test.tsx
+++ b/apps/loopover-ui/src/components/site/audit-feed.test.tsx
@@ -11,6 +11,7 @@ import {
normalizeSinceInput,
normalizeSkippedPrAuditExport,
pullRequestHref,
+ skipReasonTone,
} from "@/components/site/audit-feed-model";
import { AuditFeed } from "@/components/site/audit-feed";
@@ -70,6 +71,21 @@ describe("audit feed helpers", () => {
);
});
+ it("maps the enumerated skip reasons to their tones and degrades an unrecognized reason to the neutral info tone, never ready", () => {
+ // The four enumerated reasons keep their existing tones.
+ expect(skipReasonTone("bot_author")).toBe("info");
+ expect(skipReasonTone("not_official_gittensor_miner")).toBe("info");
+ expect(skipReasonTone("surface_off")).toBe("warn");
+ expect(skipReasonTone("maintainer_author")).toBe("warn");
+ expect(skipReasonTone("miner_detection_unavailable")).toBe("degraded");
+ expect(skipReasonTone("missing_author")).toBe("degraded");
+ // An unrecognized/legacy reason string must degrade to the neutral pill (the
+ // contributor-quality-table-model `band: string` convention), not the green "ready" tone.
+ expect(skipReasonTone("legacy_skip_reason")).toBe("info");
+ expect(skipReasonTone("legacy_skip_reason")).not.toBe("ready");
+ expect(skipReasonTone("")).toBe("info");
+ });
+
it("normalizes since input without throwing on invalid dates", () => {
expect(normalizeSinceInput("")).toBe("");
expect(normalizeSinceInput(" ")).toBe("");
@@ -121,6 +137,29 @@ describe("AuditFeed", () => {
);
});
+ it("renders an unrecognized skip reason as a neutral info pill, not the green ready tone", async () => {
+ apiFetch.mockResolvedValue({
+ ok: true,
+ data: {
+ ...SAMPLE,
+ items: [
+ {
+ repoFullName: "repo-owner/owned-repo",
+ pullNumber: 6,
+ reason: "legacy_skip_reason",
+ timestamp: "2026-05-28T00:00:04.000Z",
+ remediation: "Contact support to reclassify this legacy skip.",
+ },
+ ],
+ },
+ });
+ render();
+ const pill = await screen.findByText("legacy skip reason");
+ // "info" tone (text-mint), never the "ready" tone (text-success) that implies a healthy state.
+ expect(pill.className).toContain("text-mint");
+ expect(pill.className).not.toContain("text-success");
+ });
+
it("wraps the audit table in a keyboard-focusable, labelled scroll region with a caption and column-scoped headers (#794 a11y pattern)", async () => {
render();
await screen.findByText("repo-owner/owned-repo");