Refine evals objective selection and recommendation URL defaults

This commit is contained in:
Michael Preuss 2026-02-12 20:49:17 -08:00
parent 132f7af6ca
commit 1d03382f26
10 changed files with 138 additions and 25 deletions

View file

@ -147,7 +147,9 @@ export function MethodologyContent() {
Evals
</Link>
<span className="text-border">/</span>
<Link href="/evals/workers" className="transition-colors hover:text-foreground">
<Link
href="/evals/recommendations"
className="transition-colors hover:text-foreground">
Recommendations
</Link>
<span className="text-border">/</span>
@ -183,7 +185,7 @@ export function MethodologyContent() {
variants={fadeUpVariants}
className="mt-10 flex flex-wrap items-center gap-5">
<Link
href="/evals/workers"
href="/evals/recommendations"
className="inline-flex items-center justify-center gap-2 rounded-full border border-foreground/20 bg-foreground/5 px-5 py-2.5 text-sm font-semibold text-foreground transition-colors hover:bg-foreground/10">
View recommendations <InlineArrow />
</Link>
@ -500,7 +502,7 @@ export function MethodologyContent() {
Links
</p>
<div className="mt-4 flex flex-col gap-3">
<SmallLink href="/evals/workers" label="Recommendations" />
<SmallLink href="/evals/recommendations" label="Recommendations" />
<SmallLink href="/evals" label="Raw eval data" />
</div>
<div className="mt-5 border-t border-border/40 pt-5">

View file

@ -0,0 +1,2 @@
export { generateMetadata } from "../../../workers/[roleId]/compare/page"
export { default } from "../../../workers/[roleId]/compare/page"

View file

@ -0,0 +1,2 @@
export { generateMetadata } from "../../workers/[roleId]/page"
export { default } from "../../workers/[roleId]/page"

View file

@ -0,0 +1,94 @@
import type { Metadata } from "next"
import { Fraunces, IBM_Plex_Sans } from "next/font/google"
import { SEO } from "@/lib/seo"
import { ogImageUrl } from "@/lib/og"
import { getEngineerRoles, getAllRecommendations } from "@/lib/mock-recommendations"
import { WorkersContent } from "../workers/workers-content"
// ── SEO Metadata ────────────────────────────────────────────────────────────
const TITLE = "Build with Roo Code Cloud | Roo Code Evals"
const DESCRIPTION =
"Outcome-first, eval-backed recommendations for shipping production code. Start from your objective and pick a tradeoff."
const OG_DESCRIPTION = "Outcome-first recommendations for shipping production code"
const PATH = "/evals/recommendations"
const display = Fraunces({ subsets: ["latin"], variable: "--font-display" })
const body = IBM_Plex_Sans({ subsets: ["latin"], weight: ["400", "500", "600"], variable: "--font-body" })
export const metadata: Metadata = {
title: TITLE,
description: DESCRIPTION,
alternates: {
canonical: `${SEO.url}${PATH}`,
},
openGraph: {
title: TITLE,
description: DESCRIPTION,
url: `${SEO.url}${PATH}`,
siteName: SEO.name,
images: [
{
url: ogImageUrl(TITLE, OG_DESCRIPTION),
width: 1200,
height: 630,
alt: TITLE,
},
],
locale: SEO.locale,
type: "website",
},
twitter: {
card: SEO.twitterCard,
title: TITLE,
description: DESCRIPTION,
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
},
keywords: [
...SEO.keywords,
"AI coding",
"coding agents",
"roo code cloud",
"model recommendations",
"coding evals",
"model comparison",
"shipping code",
"prototype",
],
}
// ── Page Component ──────────────────────────────────────────────────────────
export default function RecommendationsPage() {
const roles = getEngineerRoles()
const recommendations = getAllRecommendations()
// Aggregate totals
const totalEvalRuns = recommendations.reduce((sum, recommendation) => sum + recommendation.totalEvalRuns, 0)
const totalExercises = recommendations.reduce((sum, recommendation) => sum + recommendation.totalExercises, 0)
const uniqueModels = new Set(
recommendations.flatMap((recommendation) => recommendation.allCandidates.map((candidate) => candidate.modelId)),
)
const totalModels = uniqueModels.size
const lastUpdated = recommendations
.map((r) => r.lastUpdated)
.sort()
.pop()
return (
<div className={`${body.variable} ${display.variable} [font-family:var(--font-body)]`}>
<WorkersContent
roles={roles}
recommendations={recommendations}
totalEvalRuns={totalEvalRuns}
totalExercises={totalExercises}
totalModels={totalModels}
lastUpdated={lastUpdated}
workersRootPath="/evals/recommendations"
/>
</div>
)
}

View file

@ -8,5 +8,5 @@ type PageProps = {
export default async function WorkersV2Page({ searchParams }: PageProps) {
const sp = (await searchParams) ?? {}
permanentRedirect(`/evals/workers${buildQueryString(sp)}`)
permanentRedirect(`/evals/recommendations${buildQueryString(sp)}`)
}

View file

@ -660,7 +660,7 @@ export function CandidatesContent({
totalExercises,
lastUpdated,
cloudUrls,
workersRootPath = "/evals/workers",
workersRootPath = "/evals/recommendations",
}: CandidatesContentProps) {
const searchParams = useSearchParams()
const theme = ROLE_THEMES[roleId] ?? DEFAULT_THEME

View file

@ -441,7 +441,7 @@ export function ComparisonChart({
recommendation,
role,
roleId,
workersRootPath = "/evals/workers",
workersRootPath = "/evals/recommendations",
}: ComparisonChartProps) {
const searchParams = useSearchParams()
const { allCandidates } = recommendation

View file

@ -25,7 +25,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
const title = `Compare Models — ${role.name} | Roo Code Evals`
const description = `Interactive comparison of AI models for the ${role.name} setup. Compare composite score, success rate, cost efficiency, and speed.`
const ogDescription = `Compare Models — ${role.name}`
const path = `/evals/workers/${roleId}/compare`
const path = `/evals/recommendations/${roleId}/compare`
return {
title,
@ -82,7 +82,7 @@ export default async function CompareModelsPage({ params }: PageProps) {
recommendation={recommendation}
role={recommendation.role}
roleId={roleId}
workersRootPath="/evals/workers"
workersRootPath="/evals/recommendations"
/>
)
}

View file

@ -26,7 +26,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
const title = `${role.name} — Recommended Models | Roo Code Evals`
const description = `Eval-backed recommendations for ${role.name}. Compare models by success rate, cost, and speed across 5 languages.`
const ogDescription = `${role.name} — Recommended Models`
const path = `/evals/workers/${roleId}`
const path = `/evals/recommendations/${roleId}`
return {
title,
@ -100,7 +100,7 @@ export default async function RoleCandidatesPage({ params }: PageProps) {
totalExercises={totalExercises}
lastUpdated={lastUpdated}
cloudUrls={cloudUrls}
workersRootPath="/evals/workers"
workersRootPath="/evals/recommendations"
/>
)
}

View file

@ -202,7 +202,7 @@ export function WorkersContent({
totalExercises: _totalExercises,
totalModels: _totalModels,
lastUpdated,
workersRootPath = "/evals/workers",
workersRootPath = "/evals/recommendations",
}: WorkersContentProps) {
const enableOutcomeLayer = ENABLE_OUTCOME_LAYER
const router = useRouter()
@ -214,6 +214,10 @@ export function WorkersContent({
if (!outcome) return null
return isEvalOutcomeId(outcome) ? outcome : null
}, [searchParams])
const effectiveOutcomeId = useMemo(() => {
if (selectedOutcomeId) return selectedOutcomeId
return EVAL_OUTCOMES[0]?.id ?? null
}, [selectedOutcomeId])
const selectedMode = useMemo((): EvalOptimizationMode => {
const mode = searchParams.get("mode")
@ -255,18 +259,27 @@ export function WorkersContent({
const roleById = useMemo(() => new Map(roles.map((r) => [r.id, r])), [roles])
const selectedOutcome = useMemo(() => {
if (!selectedOutcomeId) return null
return EVAL_OUTCOMES.find((o) => o.id === selectedOutcomeId) ?? null
}, [selectedOutcomeId])
if (!effectiveOutcomeId) return null
return EVAL_OUTCOMES.find((o) => o.id === effectiveOutcomeId) ?? null
}, [effectiveOutcomeId])
const setupQuery = useMemo(() => {
if (!selectedOutcomeId) return ""
if (!effectiveOutcomeId) return ""
const params = new URLSearchParams()
params.set("outcome", selectedOutcomeId)
params.set("outcome", effectiveOutcomeId)
params.set("mode", selectedMode)
const query = params.toString()
return query ? `?${query}` : ""
}, [selectedOutcomeId, selectedMode])
}, [effectiveOutcomeId, selectedMode])
const profileViewQuery = useMemo(() => {
if (!effectiveOutcomeId) return ""
const params = new URLSearchParams()
params.set("outcome", effectiveOutcomeId)
params.set("mode", selectedMode)
params.set("view", "profile")
return `?${params.toString()}`
}, [effectiveOutcomeId, selectedMode])
const isProfileView = useMemo(() => {
return searchParams.get("view") === "profile"
@ -277,22 +290,22 @@ export function WorkersContent({
"A default setup built from our eval signals. It’s a baseline, not a guarantee."
const profileHowItWorks = selectedOutcome?.builderProfile?.howItWorks ?? selectedOutcome?.whyItWorks ?? []
const objectiveDefaultModel = useMemo(() => {
if (!selectedOutcomeId) return null
return pickObjectiveDefaultModelV1(selectedOutcomeId, selectedMode)
}, [selectedOutcomeId, selectedMode])
if (!effectiveOutcomeId) return null
return pickObjectiveDefaultModelV1(effectiveOutcomeId, selectedMode)
}, [effectiveOutcomeId, selectedMode])
const objectiveDefaultModelLabel = useMemo(() => {
if (!objectiveDefaultModel?.modelId) return "—"
return formatModelIdForUi(objectiveDefaultModel.modelId)
}, [objectiveDefaultModel])
const examplePrompt = selectedOutcome?.builderProfile?.examplePrompt ?? ""
const cloudSetupHref = useMemo(() => {
if (!selectedOutcomeId) return "/cloud-agents/setup"
if (!effectiveOutcomeId) return "/cloud-agents/setup"
const params = new URLSearchParams()
params.set("outcome", selectedOutcomeId)
params.set("outcome", effectiveOutcomeId)
params.set("mode", selectedMode)
if (examplePrompt) params.set("prompt", examplePrompt)
return `/cloud-agents/setup?${params.toString()}`
}, [examplePrompt, selectedMode, selectedOutcomeId])
}, [examplePrompt, selectedMode, effectiveOutcomeId])
const profileCapabilities = useMemo(() => {
if (!selectedOutcome) return []
@ -741,7 +754,7 @@ export function WorkersContent({
<motion.div className="mt-8 space-y-2" variants={containerVariants}>
{EVAL_OUTCOMES.map((outcome) => {
const Icon = outcome.icon
const isSelected = outcome.id === selectedOutcomeId
const isSelected = outcome.id === effectiveOutcomeId
return (
<motion.button
@ -946,7 +959,7 @@ export function WorkersContent({
<ArrowRight className="size-4" />
</Link>
<Link
href={`${workersRootPath}${setupQuery}&view=profile#outcomes`}
href={`${workersRootPath}${profileViewQuery}#outcomes`}
className="inline-flex flex-1 items-center justify-center gap-2 rounded-2xl border border-border/60 bg-background/10 px-4 py-3 text-sm font-semibold text-muted-foreground transition-colors hover:border-border hover:bg-background/15 hover:text-foreground">
Learn more / customize
<ArrowRight className="size-4" />