You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
GET /v1/public/stats publishes fleetAccuracy.basis — added by #9168 and computed at src/review/public-stats.ts:600 as fleet.fleetFramingEligible ? "fleet" : "single_instance_self_report".
Its whole purpose is stated in src/orb/analytics.ts:43-49:
"Publishing those numbers under fleet framing invites a reader to treat one party's self-report as
independent corroboration of that same party's guarantee. The numbers stay published — they are
real — but basis says which of the two they are."
No UI surface reads it. Both public consumers hardcode fleet framing:
apps/loopover-ui/src/components/site/proof-of-power-stats.tsx:115-117 gates on instanceCount > 0 && accuracyPct != null — true at one instance — and line 177 renders the
hint ending · ${instanceCount} self-hosted instance${instanceCount === 1 ? "" : "s"}, with the
file comment at line 109 calling it "the live, fleet-wide accuracy".
apps/loopover-ui/src/components/site/fairness-report-page.tsx:75-79 uses the identical instanceCount > 0 gate, line 117 renders across N self-hosted instances, and lines 158-164
render an unconditional methodology paragraph headed "Why the fleet number, not just our own
repos" — exactly the overclaim basis exists to prevent, shown verbatim at instanceCount === 1.
The UI's hand-authored PublicStats type
(apps/loopover-ui/src/components/site/proof-of-power-stats-model.ts:31-68) does not declare basis
at all, so nothing in the app can even reach the field today. The same type has two further concrete
drifts from PublicStatsSchema:
accuracyTrend items are typed merged: number; closed: number; reversed: number
(proof-of-power-stats-model.ts:70-76) but the schema declares all three z.number().nullable() (src/openapi/schemas.ts:170-178). fairness-report-page.tsx:242-250
already renders week.merged != null ? … : "—" for all three — the code proves the type is wrong.
decidedCount?: number (proof-of-power-stats-model.ts:52) vs the schema's decidedCount: z.number().nullable() (src/openapi/schemas.ts:154).
This is not #9282. #9282 decides and builds the derivation mechanism (z.infer vs openapi-typescript) plus a drift check, and explicitly says not to migrate surfaces wholesale. This
issue fixes one specific already-shipped mislabel and the two specific type errors that let it hide.
Correct accuracyTrend's item type to merged: number | null; closed: number | null; reversed: number | null and decidedCount to decidedCount?: number | null, matching src/openapi/schemas.ts:154 and :170-178.
Add a single exported pure helper to proof-of-power-stats-model.ts — signature exactly export function isFleetBasis(fleetAccuracy: PublicStats["fleetAccuracy"] | undefined): boolean —
returning true only when fleetAccuracy?.basis === "fleet". Both surfaces must call it; neither
may re-derive the check inline.
proof-of-power-stats.tsx: when the accuracy figure is shown and isFleetBasis(...) is false,
the tile hint must say the figure is one self-hosted instance's own self-report rather than a fleet
aggregate. The numeric value itself must still be displayed — basis changes the label, never the
number.
fairness-report-page.tsx: apply the same labelling to the "Decision accuracy" card's caption
(line 117), and render the "Why the fleet number, not just our own repos" paragraph
(lines 158-164) only when isFleetBasis(...) is true.
No other behaviour changes. Do not alter the fleetEligible numeric-eligibility gate, the gamingFlagsCaught card, or any endpoint.
⚠️ Required pattern: mirror the existing #9068gamingFlagsCaught treatment already in this
codebase — fairness-report-page.tsx:126-136 and the proof-of-power-stats.tsx:175-177 comment —
where a backend-supplied disambiguating field changes the copy, never the number. What does NOT
satisfy this issue: hiding the accuracy tile entirely below the fleet-framing floor; deriving the
distinction client-side from instanceCount instead of reading basis; a differently-named or
differently-shaped helper; or fixing one of the two surfaces and leaving the other.
Deliverables
PublicStats["fleetAccuracy"] in proof-of-power-stats-model.ts declares basis?: "fleet" | "single_instance_self_report", decidedCount?: number | null, and accuracyTrend
items with all three of merged/closed/reversed typed number | null.
isFleetBasis() is exported from proof-of-power-stats-model.ts with the exact signature
above, with unit cases for "fleet", "single_instance_self_report", a missing basis, and
an undefinedfleetAccuracy, in apps/loopover-ui/src/components/site/proof-of-power-stats.test.tsx.
ProofOfPowerStats given fleetAccuracy.basis === "single_instance_self_report" renders the
accuracy percentage AND a hint that does not claim a fleet, asserted by a new case in proof-of-power-stats.test.tsx.
FairnessReportPage given basis === "single_instance_self_report" does not render the text Why the fleet number, and given basis === "fleet" does render it — both asserted in apps/loopover-ui/src/components/site/fairness-report-page.test.tsx.
A regression case asserting that an accuracyTrend week with merged: null, closed: null, reversed: null still renders (as —) without a type-level or runtime break.
All Deliverables above are required in a single PR. A PR that satisfies only some of them — for
example adding basis to the type (Deliverable 1) without changing either surface's copy — does not
resolve this issue.
Test Coverage Requirements
apps/** is outside Codecov's coverage.include (vitest.config.ts), so codecov/patch does not
gate this change; real tests are still required and npm run ui:test is a required CI check. Both
arms of isFleetBasis() and of every conditional it newly guards must be tested — "fleet" and "single_instance_self_report", plus the absent-field arm that an older backend produces. The single_instance_self_report render assertion on each surface is the named regression test.
Expected Outcome
At fewer than FLEET_FRAMING_MIN_INSTANCES registered instances, the homepage tile and the /fairness page disclose that the accuracy figure is one operator's self-report rather than a fleet
aggregate — the exact distinction src/orb/analytics.ts:43-49 says the field exists to publish — and
the hand-typed PublicStats stops contradicting PublicStatsSchema on three fields.
Context
GET /v1/public/statspublishesfleetAccuracy.basis— added by #9168 and computed atsrc/review/public-stats.ts:600asfleet.fleetFramingEligible ? "fleet" : "single_instance_self_report".Its whole purpose is stated in
src/orb/analytics.ts:43-49:No UI surface reads it. Both public consumers hardcode fleet framing:
apps/loopover-ui/src/components/site/proof-of-power-stats.tsx:115-117gates oninstanceCount > 0 && accuracyPct != null— true at one instance — and line 177 renders thehint ending
· ${instanceCount} self-hosted instance${instanceCount === 1 ? "" : "s"}, with thefile comment at line 109 calling it "the live, fleet-wide accuracy".
apps/loopover-ui/src/components/site/fairness-report-page.tsx:75-79uses the identicalinstanceCount > 0gate, line 117 rendersacross N self-hosted instances, and lines 158-164render an unconditional methodology paragraph headed "Why the fleet number, not just our own
repos" — exactly the overclaim
basisexists to prevent, shown verbatim atinstanceCount === 1.The UI's hand-authored
PublicStatstype(
apps/loopover-ui/src/components/site/proof-of-power-stats-model.ts:31-68) does not declarebasisat all, so nothing in the app can even reach the field today. The same type has two further concrete
drifts from
PublicStatsSchema:accuracyTrenditems are typedmerged: number; closed: number; reversed: number(
proof-of-power-stats-model.ts:70-76) but the schema declares all threez.number().nullable()(src/openapi/schemas.ts:170-178).fairness-report-page.tsx:242-250already renders
week.merged != null ? … : "—"for all three — the code proves the type is wrong.decidedCount?: number(proof-of-power-stats-model.ts:52) vs the schema'sdecidedCount: z.number().nullable()(src/openapi/schemas.ts:154).This is not #9282. #9282 decides and builds the derivation mechanism (
z.infervsopenapi-typescript) plus a drift check, and explicitly says not to migrate surfaces wholesale. Thisissue fixes one specific already-shipped mislabel and the two specific type errors that let it hide.
Requirements
basis: "fleet" | "single_instance_self_report"toPublicStats["fleetAccuracy"]inapps/loopover-ui/src/components/site/proof-of-power-stats-model.ts, declared optional(
basis?:) with a comment matching the existing optional-field convention in that same type(see the
#8829 fields — optional-chained at the render sitecomment at line 33), so an olderbackend that predates orb(public-stats): fleetAccuracy has no minimum instanceCount — at N=1 it publishes one operator's self-report as a fleet aggregate, and the anti-gaming detector it cites cannot fire #9168 does not break the render.
accuracyTrend's item type tomerged: number | null; closed: number | null; reversed: number | nullanddecidedCounttodecidedCount?: number | null, matchingsrc/openapi/schemas.ts:154and:170-178.proof-of-power-stats-model.ts— signature exactlyexport function isFleetBasis(fleetAccuracy: PublicStats["fleetAccuracy"] | undefined): boolean—returning
trueonly whenfleetAccuracy?.basis === "fleet". Both surfaces must call it; neithermay re-derive the check inline.
proof-of-power-stats.tsx: when the accuracy figure is shown andisFleetBasis(...)isfalse,the tile hint must say the figure is one self-hosted instance's own self-report rather than a fleet
aggregate. The numeric value itself must still be displayed —
basischanges the label, never thenumber.
fairness-report-page.tsx: apply the same labelling to the "Decision accuracy" card's caption(line 117), and render the "Why the fleet number, not just our own repos" paragraph
(lines 158-164) only when
isFleetBasis(...)istrue.fleetEligiblenumeric-eligibility gate, thegamingFlagsCaughtcard, or any endpoint.Deliverables
PublicStats["fleetAccuracy"]inproof-of-power-stats-model.tsdeclaresbasis?: "fleet" | "single_instance_self_report",decidedCount?: number | null, andaccuracyTrenditems with all three of
merged/closed/reversedtypednumber | null.isFleetBasis()is exported fromproof-of-power-stats-model.tswith the exact signatureabove, with unit cases for
"fleet","single_instance_self_report", a missingbasis, andan
undefinedfleetAccuracy, inapps/loopover-ui/src/components/site/proof-of-power-stats.test.tsx.ProofOfPowerStatsgivenfleetAccuracy.basis === "single_instance_self_report"renders theaccuracy percentage AND a hint that does not claim a fleet, asserted by a new case in
proof-of-power-stats.test.tsx.FairnessReportPagegivenbasis === "single_instance_self_report"does not render the textWhy the fleet number, and givenbasis === "fleet"does render it — both asserted inapps/loopover-ui/src/components/site/fairness-report-page.test.tsx.accuracyTrendweek withmerged: null, closed: null, reversed: nullstill renders (as—) without a type-level or runtime break.All Deliverables above are required in a single PR. A PR that satisfies only some of them — for
example adding
basisto the type (Deliverable 1) without changing either surface's copy — does notresolve this issue.
Test Coverage Requirements
apps/**is outside Codecov'scoverage.include(vitest.config.ts), socodecov/patchdoes notgate this change; real tests are still required and
npm run ui:testis a required CI check. Botharms of
isFleetBasis()and of every conditional it newly guards must be tested —"fleet"and"single_instance_self_report", plus the absent-field arm that an older backend produces. Thesingle_instance_self_reportrender assertion on each surface is the named regression test.Expected Outcome
At fewer than
FLEET_FRAMING_MIN_INSTANCESregistered instances, the homepage tile and the/fairnesspage disclose that the accuracy figure is one operator's self-report rather than a fleetaggregate — the exact distinction
src/orb/analytics.ts:43-49says the field exists to publish — andthe hand-typed
PublicStatsstops contradictingPublicStatsSchemaon three fields.Links & Resources
src/openapi/schemas.ts:143-166(PublicStatsSchema.fleetAccuracy),:169-178(accuracyTrend)src/review/public-stats.ts:277-283,:600src/orb/analytics.ts:43-49,:155-163,:402apps/loopover-ui/src/components/site/proof-of-power-stats-model.ts:29-76apps/loopover-ui/src/components/site/proof-of-power-stats.tsx:109-188apps/loopover-ui/src/components/site/fairness-report-page.tsx:73-79,:110-122,:150-165