injection — return the canonical + redirect-safe metadata so
// crawlers that peek at the response before the 308 fires still see the
// right canonical target.
- if (await isHlBuilderSlug(slug)) {
+ if (await isHlBuilderWithHistory(slug)) {
const canonicalUrl = `${SITE.url}/hyperliquid/${slug}`;
return {
alternates: { canonical: canonicalUrl },
@@ -145,7 +145,7 @@ export default async function ProviderPage({
// frontends (12-month focus chart + peer group + KPI strip). Redirect
// /products/ straight there so backlinks + old SERP entries
// land on the richer hub without splitting rank signal across two URLs.
- if (await isHlBuilderSlug(slug)) {
+ if (await isHlBuilderWithHistory(slug)) {
redirect(`/hyperliquid/${slug}`);
}
const p = await getProvider(slug);
@@ -158,7 +158,7 @@ export default async function ProviderPage({
// on-node harness has data for. Cheap (6 Prom scalars in parallel) and
// gracefully degrades to a hidden section when Prom is unreachable or
// the slug isn't an HL builder.
- const isHlBuilder = await isHlBuilderSlug(p.slug);
+ const isHlBuilder = await isHlBuilderWithHistory(p.slug);
const hlStats = isHlBuilder ? await fetchHlBuilderStats(p.slug) : null;
// Prediction-market deep-dive: if the slug is a tracked PM venue or
diff --git a/src/components/hl-cohort-leaderboard.tsx b/src/components/hl-cohort-leaderboard.tsx
index 2a794b14..272f987f 100644
--- a/src/components/hl-cohort-leaderboard.tsx
+++ b/src/components/hl-cohort-leaderboard.tsx
@@ -114,6 +114,12 @@ export function HlCohortLeaderboard({
{filtered.map((r, i) => {
const hist = historyBySlug?.get(r.slug);
+ // Only builders that have a `hist` entry actually have a
+ // detail page at /hyperliquid/ (the page component
+ // 404s when the history blob has no matching frontend, see
+ // hyperliquid/[slug]/page.tsx). Skip the link so Ahrefs +
+ // Bing don't queue up crawl paths that dead-end at 404.
+ const hasDetailPage = historyBySlug ? !!hist : true;
return (
);
diff --git a/src/lib/hl-builder-stats.ts b/src/lib/hl-builder-stats.ts
index dd793574..c4513369 100644
--- a/src/lib/hl-builder-stats.ts
+++ b/src/lib/hl-builder-stats.ts
@@ -70,6 +70,21 @@ export async function isHlBuilderSlug(slug: string): Promise {
return cachedSlugs.has(slug);
}
+/**
+ * Same as `isHlBuilderSlug` but ALSO requires the slug to be present in
+ * the current history blob. Used before redirecting /products/ to
+ * /hyperliquid/ so we never send crawlers or users into a 404: a
+ * builder that's in the spec but not in the last-12-months history blob
+ * (dormant, new, or paused) would otherwise 404 at the redirect target.
+ * The 12-month history is the source of truth for what /hyperliquid/
+ * can actually render (see hyperliquid/[slug]/page.tsx:83-85).
+ */
+export async function isHlBuilderWithHistory(slug: string): Promise {
+ if (!(await isHlBuilderSlug(slug))) return false;
+ const history = await fetchHlHistory();
+ return !!history?.frontends.some((f) => f.slug === slug);
+}
+
function promUrl(): string | null {
return process.env.PROMETHEUS_URL?.trim() || null;
}