diff --git a/apps/loopover-ui/src/components/site/fairness-report-page.test.tsx b/apps/loopover-ui/src/components/site/fairness-report-page.test.tsx
index 8c3624a5b..a53611c88 100644
--- a/apps/loopover-ui/src/components/site/fairness-report-page.test.tsx
+++ b/apps/loopover-ui/src/components/site/fairness-report-page.test.tsx
@@ -307,6 +307,65 @@ describe("FairnessReportPage (#fairness-analytics)", () => {
);
});
+ it("#9673: renders the fleet-methodology paragraph when basis is a genuine fleet aggregate", async () => {
+ apiFetch.mockResolvedValue({ ok: true, data: FIXTURE, status: 200, durationMs: 10 });
+ renderWithClient();
+
+ await waitFor(() => expect(screen.getByText("Decision accuracy")).toBeTruthy());
+ expect(screen.getByText(/Why the fleet number, not just our own repos/)).toBeTruthy();
+ });
+
+ it("#9673: hides the fleet-methodology paragraph when basis is one instance's own self-report", async () => {
+ apiFetch.mockResolvedValue({
+ ok: true,
+ data: {
+ ...FIXTURE,
+ fleetAccuracy: {
+ ...FIXTURE.fleetAccuracy,
+ instanceCount: 1,
+ basis: "single_instance_self_report",
+ },
+ },
+ status: 200,
+ durationMs: 10,
+ });
+ renderWithClient();
+
+ await waitFor(() => expect(screen.getByText("Decision accuracy")).toBeTruthy());
+ expect(screen.queryByText(/Why the fleet number, not just our own repos/)).toBeNull();
+ // The card caption also drops the fleet framing rather than reporting "1 self-hosted instance" as corroboration.
+ expect(
+ screen.getByText(
+ "self-reported by one self-hosted instance, not corroborated across operators, last 90 days",
+ ),
+ ).toBeTruthy();
+ });
+
+ it("REGRESSION #9673: a weekly trend row with merged/closed/reversed all null still renders as — without throwing", async () => {
+ apiFetch.mockResolvedValue({
+ ok: true,
+ data: {
+ ...FIXTURE,
+ accuracyTrend: [
+ {
+ weekStart: "2026-01-05",
+ merged: null,
+ closed: null,
+ reversed: null,
+ accuracyPct: null,
+ },
+ ],
+ },
+ status: 200,
+ durationMs: 10,
+ });
+ renderWithClient();
+
+ await waitFor(() => expect(screen.getByText("Weekly trend")).toBeTruthy());
+ const row = screen.getByText("2026-01-05").closest("tr")!;
+ expect(row.textContent).toBe("2026-01-05————");
+ });
+
it("#9068: renders the insufficient-instances state (not a fabricated zero) when gamingFlagsCaught is null", async () => {
apiFetch.mockResolvedValue({
ok: true,
diff --git a/apps/loopover-ui/src/components/site/fairness-report-page.tsx b/apps/loopover-ui/src/components/site/fairness-report-page.tsx
index f11b583f0..e8eb3a659 100644
--- a/apps/loopover-ui/src/components/site/fairness-report-page.tsx
+++ b/apps/loopover-ui/src/components/site/fairness-report-page.tsx
@@ -7,7 +7,7 @@ import { TableScroll } from "@/components/site/data-table";
import { Card, Section } from "@/components/site/primitives";
import { StateBoundary } from "@/components/site/state-views";
import { Skeleton } from "@/components/ui/skeleton";
-import type { PublicStats } from "@/components/site/proof-of-power-stats-model";
+import { isFleetBasis, type PublicStats } from "@/components/site/proof-of-power-stats-model";
// Fairness report (#fairness-analytics): a deeper, linkable page behind the homepage's "Decision accuracy" tile.
// Reads the SAME /v1/public/stats payload proof-of-power-stats.tsx does (no new endpoint) -- this page just
@@ -126,15 +126,19 @@ export function FairnessReportPage() {
{headlineAccuracyPct != null ? `${pctFmt.format(headlineAccuracyPct)}%` : "—"}
+ {/* #9673: `fleetEligible` only gates volume; `basis` says whether this number is
+ corroborated across operators or one instance's own self-report -- say which. */}
{fleetEligible
- ? `across ${intFmt.format(data.fleetAccuracy.instanceCount)} self-hosted instance${data.fleetAccuracy.instanceCount === 1 ? "" : "s"}, last ${data.fleetAccuracy.windowDays} days`
+ ? isFleetBasis(data.fleetAccuracy)
+ ? `across ${intFmt.format(data.fleetAccuracy.instanceCount)} self-hosted instance${data.fleetAccuracy.instanceCount === 1 ? "" : "s"}, last ${data.fleetAccuracy.windowDays} days`
+ : `self-reported by one self-hosted instance, not corroborated across operators, last ${data.fleetAccuracy.windowDays} days`
: data.totals.reversed > 0
? `${intFmt.format(data.totals.reversed)} human-reversed, lifetime`
: "reversal-grounded, lifetime"}
{/* #9168 computes `basis` precisely so this number is not read as corroborated-across-operators
when it is one operator's own disclosed outcomes; the page used to drop the field entirely. */}
- {fleetEligible && data.fleetAccuracy.basis === "single_instance_self_report" ? (
+ {fleetEligible && !isFleetBasis(data.fleetAccuracy) ? (
Self-reported by that single instance, not corroborated across operators
{data.fleetAccuracy.decidedCount != null
@@ -189,13 +193,17 @@ export function FairnessReportPage() {
or a self-assessment; both are counted after the fact from what actually happened on
GitHub, which is also why the two can differ.
-
-
- Why the fleet number, not just our own repos:
- {" "}
- the self-hosted instance count above reflects the live fleet running ORB today, not
- a historical snapshot of LoopOver's own repos alone.
-
+ {/* #9673: this paragraph asserts fleet corroboration -- render it only when `basis` says the
+ headline actually IS a fleet aggregate, not one instance's own self-report. */}
+ {isFleetBasis(data.fleetAccuracy) ? (
+
+
+ Why the fleet number, not just our own repos:
+ {" "}
+ the self-hosted instance count above reflects the live fleet running ORB today,
+ not a historical snapshot of LoopOver's own repos alone.
+
+ ) : null}
{data.byProject.length > 0 ? (
diff --git a/apps/loopover-ui/src/components/site/proof-of-power-stats-model.ts b/apps/loopover-ui/src/components/site/proof-of-power-stats-model.ts
index 09b18b55f..84ae08076 100644
--- a/apps/loopover-ui/src/components/site/proof-of-power-stats-model.ts
+++ b/apps/loopover-ui/src/components/site/proof-of-power-stats-model.ts
@@ -12,6 +12,13 @@ import type { PublicStatsSchema } from "@loopover/contract/public-api";
export type { PublicRulePrecision } from "@loopover/contract/public-api";
export type PublicStats = z.infer;
+/** Whether `fleetAccuracy` is genuine cross-operator corroboration rather than one instance's own
+ * self-report (#9673) -- the distinction `basis` exists to publish (src/orb/analytics.ts). Both public
+ * surfaces that read `fleetAccuracy` call this rather than re-deriving the check inline. */
+export function isFleetBasis(fleetAccuracy: PublicStats["fleetAccuracy"] | undefined): boolean {
+ return fleetAccuracy?.basis === "fleet";
+}
+
/** Relative "updated Ns ago" label from the payload's updatedAt (mirrors MetaStrip's freshness logic). */
export function formatStatsAgo(updatedAt: string | null, nowMs: number): string {
if (!updatedAt) return "just now";
diff --git a/apps/loopover-ui/src/components/site/proof-of-power-stats.test.tsx b/apps/loopover-ui/src/components/site/proof-of-power-stats.test.tsx
index 775f3d01f..77d80653e 100644
--- a/apps/loopover-ui/src/components/site/proof-of-power-stats.test.tsx
+++ b/apps/loopover-ui/src/components/site/proof-of-power-stats.test.tsx
@@ -31,6 +31,7 @@ import { ProofOfPowerStats } from "@/components/site/proof-of-power-stats";
import {
formatStatsAgo,
formatTimeSaved,
+ isFleetBasis,
type PublicStats,
} from "@/components/site/proof-of-power-stats-model";
@@ -150,6 +151,24 @@ describe("formatStatsAgo", () => {
});
});
+describe("isFleetBasis", () => {
+ it('is true only for basis: "fleet"', () => {
+ expect(isFleetBasis({ ...PAYLOAD.fleetAccuracy, basis: "fleet" })).toBe(true);
+ });
+ it('is false for basis: "single_instance_self_report"', () => {
+ expect(isFleetBasis({ ...PAYLOAD.fleetAccuracy, basis: "single_instance_self_report" })).toBe(
+ false,
+ );
+ });
+ it("is false when fleetAccuracy has no basis field (deployment predating #9168)", () => {
+ const { basis: _omitted, ...withoutBasis } = PAYLOAD.fleetAccuracy;
+ expect(isFleetBasis(withoutBasis as PublicStats["fleetAccuracy"])).toBe(false);
+ });
+ it("is false when fleetAccuracy itself is undefined (deployment predating #9168 entirely)", () => {
+ expect(isFleetBasis(undefined)).toBe(false);
+ });
+});
+
describe("formatTimeSaved", () => {
it("uses days at scale, hours below 2 days, minutes below an hour", () => {
expect(formatTimeSaved(54160)).toEqual({ value: 38, unit: "days" }); // 2708 × 20 min
@@ -237,6 +256,7 @@ describe("ProofOfPowerStats", () => {
instanceCount: 3,
windowDays: 90,
gamingFlagsCaught: 2,
+ basis: "fleet",
},
},
});
@@ -255,6 +275,35 @@ describe("ProofOfPowerStats", () => {
expect(screen.getAllByRole("img", { name: "Trend over the last 8 weeks" })).toHaveLength(3);
});
+ it("REGRESSION #9673: discloses a single-instance self-report rather than presenting it as fleet corroboration", async () => {
+ apiFetch.mockResolvedValue({
+ ok: true,
+ status: 200,
+ durationMs: 1,
+ data: {
+ ...PAYLOAD,
+ fleetAccuracy: {
+ ...PAYLOAD.fleetAccuracy,
+ accuracyPct: 92,
+ instanceCount: 1,
+ coveragePct: null,
+ basis: "single_instance_self_report",
+ },
+ },
+ });
+ renderWithClient();
+ await screen.findByText("Decision accuracy");
+ // The number itself is still shown -- basis changes the label, never the figure.
+ expect(screen.getByText("92%")).toBeTruthy();
+ expect(
+ screen.getByText(
+ "merge/close calls confirmed by outcome · self-reported by one self-hosted instance, not corroborated across operators",
+ ),
+ ).toBeTruthy();
+ // Never the fleet-count phrasing that would overclaim cross-operator corroboration.
+ expect(screen.queryByText(/1 self-hosted instance$/)).toBeNull();
+ });
+
it("#9050: disambiguates the published guarantee's coverage as the AI-judged sub-population, with the backfilled-vs-live split", async () => {
apiFetch.mockResolvedValue({
ok: true,
@@ -268,6 +317,7 @@ describe("ProofOfPowerStats", () => {
instanceCount: 3,
windowDays: 90,
gamingFlagsCaught: null, // #9068: below the fleet's own eligibility floor
+ basis: "fleet",
guaranteed: {
close: {
alpha: 0.05,
diff --git a/apps/loopover-ui/src/components/site/proof-of-power-stats.tsx b/apps/loopover-ui/src/components/site/proof-of-power-stats.tsx
index 8ef0ef84f..74a8099be 100644
--- a/apps/loopover-ui/src/components/site/proof-of-power-stats.tsx
+++ b/apps/loopover-ui/src/components/site/proof-of-power-stats.tsx
@@ -10,6 +10,7 @@ import { Sparkline } from "@/components/site/sparkline";
import {
formatStatsAgo,
formatTimeSaved,
+ isFleetBasis,
toTrendPoints,
type PublicStats,
} from "@/components/site/proof-of-power-stats-model";
@@ -148,6 +149,10 @@ export function ProofOfPowerStats({ className }: { className?: string }) {
const fleetEligible =
(data.fleetAccuracy?.instanceCount ?? 0) > 0 && data.fleetAccuracy?.accuracyPct != null;
const displayedAccuracyPct = fleetEligible ? data.fleetAccuracy!.accuracyPct : totals.accuracyPct;
+ // #9673: `fleetEligible` only gates volume (is there enough to show a number); `basis` says whether that
+ // number is corroborated across operators or one instance's own self-report -- the hint below must say
+ // which, without changing whether or what number is shown.
+ const accuracyIsFleetBasis = fleetEligible && isFleetBasis(data.fleetAccuracy);
const latestReuseRatePct =
data.reuseRateTrend.length > 0
? data.reuseRateTrend[data.reuseRateTrend.length - 1]!.reuseRatePct
@@ -207,7 +212,7 @@ export function ProofOfPowerStats({ className }: { className?: string }) {
// signals) -- plus the backfilled-vs-live split behind the evidence, when supplied.
// #9068: gamingFlagsCaught is null (not 0) below the fleet's own eligibility floor, so the
// suffix is omitted entirely rather than rendering a structural zero as "checked, found none".
- `merge/close calls confirmed by outcome${data.fleetAccuracy.coveragePct != null ? ` · at ${data.fleetAccuracy.coveragePct}% coverage` : ""}${data.fleetAccuracy.guaranteed?.close ? ` · closes ≥${Math.round((1 - data.fleetAccuracy.guaranteed.close.alpha) * 1000) / 10}% guaranteed at ${data.fleetAccuracy.guaranteed.close.aiJudgedCoveragePct}% coverage of AI-judged closes${data.fleetAccuracy.guaranteed.close.backfilledPct != null ? ` (${data.fleetAccuracy.guaranteed.close.backfilledPct}% backfilled)` : ""}` : ""} · ${intFmt.format(data.fleetAccuracy.instanceCount)} self-hosted instance${data.fleetAccuracy.instanceCount === 1 ? "" : "s"}${data.fleetAccuracy.gamingFlagsCaught != null && data.fleetAccuracy.gamingFlagsCaught > 0 ? ` · ${intFmt.format(data.fleetAccuracy.gamingFlagsCaught)} gaming pattern${data.fleetAccuracy.gamingFlagsCaught === 1 ? "" : "s"} flagged` : ""}`
+ `merge/close calls confirmed by outcome${data.fleetAccuracy.coveragePct != null ? ` · at ${data.fleetAccuracy.coveragePct}% coverage` : ""}${data.fleetAccuracy.guaranteed?.close ? ` · closes ≥${Math.round((1 - data.fleetAccuracy.guaranteed.close.alpha) * 1000) / 10}% guaranteed at ${data.fleetAccuracy.guaranteed.close.aiJudgedCoveragePct}% coverage of AI-judged closes${data.fleetAccuracy.guaranteed.close.backfilledPct != null ? ` (${data.fleetAccuracy.guaranteed.close.backfilledPct}% backfilled)` : ""}` : ""}${accuracyIsFleetBasis ? ` · ${intFmt.format(data.fleetAccuracy.instanceCount)} self-hosted instance${data.fleetAccuracy.instanceCount === 1 ? "" : "s"}` : " · self-reported by one self-hosted instance, not corroborated across operators"}${data.fleetAccuracy.gamingFlagsCaught != null && data.fleetAccuracy.gamingFlagsCaught > 0 ? ` · ${intFmt.format(data.fleetAccuracy.gamingFlagsCaught)} gaming pattern${data.fleetAccuracy.gamingFlagsCaught === 1 ? "" : "s"} flagged` : ""}`
: totals.reversed > 0
? `${intFmt.format(totals.reversed)} human-reversed`
: "reversal-grounded"