import type { Metadata } from "next"; import Link from "next/link"; import { RedundancyPanel } from "@/components/market/redundancy-panel"; import { Empty, Page, PageHeader, Pill, Section, Stat } from "@/components/ui/section"; import { api } from "@/lib/api"; import { COVERAGE_STATUS_LABEL, COVERAGE_STATUS_TONE, TIER_LABEL, TIER_TARGET } from "@/lib/coverage"; import { ASSET_CLASS_LABEL, cx, instrumentHref } from "@/lib/format"; import type { CoverageReport, CoverageTier } from "@/lib/types"; export const metadata: Metadata = { title: "Source coverage", description: "How many independent source families observe each instrument, redundancy targets by tier, the source-expansion queue and statistically inferred shared upstreams.", alternates: { canonical: "/coverage" }, }; export const dynamic = "force-dynamic"; const TIERS: CoverageTier[] = ["A", "B", "C", "D"]; const CLASSES = ["EQUITY", "ETF", "INDEX", "CRYPTO", "FOREX", "COMMODITY", "FUTURE", "TREASURY", "BOND", "INTEREST_RATE"]; export default async function CoveragePage({ searchParams }: { searchParams: Promise> }) { const sp = await searchParams; const tier = typeof sp.tier === "string" && TIERS.includes(sp.tier as CoverageTier) ? (sp.tier as CoverageTier) : undefined; const cls = typeof sp.asset_class === "string" && CLASSES.includes(sp.asset_class) ? sp.asset_class : undefined; const qs = new URLSearchParams({ limit: "200" }); if (tier) qs.set("tier", tier); if (cls) qs.set("asset_class", cls); const [report, global] = await Promise.all([api(`/v1/coverage?${qs}`), tier || cls ? api("/v1/coverage?limit=1") : null]); const s = report.summary; const g = (global ?? report).summary; const href = (t?: CoverageTier, c?: string) => { const p = new URLSearchParams(); if (t) p.set("tier", t); if (c) p.set("asset_class", c); const q = p.toString(); return `/coverage${q ? `?${q}` : ""}`; }; const byClass = Object.entries(s.byAssetClass).sort((a, b) => b[1].instruments - a[1].instruments); return ( How it is computed → } />

Redundancy targets by tier

{TIERS.map((t) => { const tt = g.tiers[t]; return (
{t}
{TIER_LABEL[t].replace(/^Tier [A-D] · /, "")}
{report.targets[t] ?? TIER_TARGET[t]}
= 90 ? "bg-positive" : tt.attainment >= 50 ? "bg-accent" : "bg-warning")} style={{ width: `${Math.min(100, tt.attainment)}%` }} />
{tt.covered} / {tt.instruments}
{tt.attainment.toFixed(1)}% covered
); })}
{byClass.map(([k, v]) => ( ))}
Asset class Quoted Multi-source Share Mean families
{ASSET_CLASS_LABEL[k] ?? k} {v.instruments} {v.multiSource} = 0.9 ? "bg-positive" : v.multiSource ? "bg-accent" : "bg-stale")} style={{ width: `${v.instruments ? (v.multiSource / v.instruments) * 100 : 0}%` }} /> {v.instruments ? `${Math.round((v.multiSource / v.instruments) * 100)}%` : "—"} {v.meanFamilies.toFixed(2)} queue →
{report.queue.length} {report.queue.length >= 200 ? "+" : ""} } >
Tier all {TIERS.map((t) => ( {t} ))} Class all {CLASSES.filter((c) => s.byAssetClass[c] || c === cls).map((c) => ( {ASSET_CLASS_LABEL[c] ?? c} ))}
{report.queue.length ? (
{report.queue.map((q, i) => ( ))}
# Instrument Tier Families / target Missing Observations Proxies · validators Class Status Score
{i + 1} {q.symbol} {q.name} {q.tier} {q.families} / {q.target.families} {q.gap > 0 ? `−${q.gap}` : "—"} {q.observations} {q.proxies} · {q.validators} {ASSET_CLASS_LABEL[q.assetClass] ?? q.assetClass} {q.comparability ? {q.comparability.toLowerCase()} class : null} {COVERAGE_STATUS_LABEL[q.status]} {Math.round(q.score * 100)}
) : ( Every instrument in this selection meets its redundancy target. )}

A family is a group of sources believed to share an upstream (declared, or inferred statistically). Proxies (stablecoin markets, crosses) and validators (restricted-rights sources) confirm a value but never count as independent families. Score = 65 % family attainment + 35 % observation attainment.

{report.lineage.length ? (
{report.lineage.map((p) => ( ))}
Source A Source B Similarity Aligned samples Instruments
{p.sourceA} {p.sourceB} {(p.similarity * 100).toFixed(1)}% {p.samples.toLocaleString("en-US")} {p.instruments}
) : ( No pair of sources currently looks like a shared upstream. The lineage engine recomputes every five minutes from live observations. )}
=3"]} sub="Tier-A target" /> =5"]} sub="most observed" />
); }